##// END OF EJS Templates
py3: workaround comparing NoneType and integers...
Pulkit Goyal -
r37543:a7de62ad default
parent child Browse files
Show More
@@ -1,3660 +1,3668 b''
1 # mq.py - patch queues for mercurial
1 # mq.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''manage a stack of patches
8 '''manage a stack of patches
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use :hg:`help COMMAND` for more details)::
17 Common tasks (use :hg:`help COMMAND` for more details)::
18
18
19 create new patch qnew
19 create new patch qnew
20 import existing patch qimport
20 import existing patch qimport
21
21
22 print patch series qseries
22 print patch series qseries
23 print applied patches qapplied
23 print applied patches qapplied
24
24
25 add known patch to applied stack qpush
25 add known patch to applied stack qpush
26 remove patch from applied stack qpop
26 remove patch from applied stack qpop
27 refresh contents of top applied patch qrefresh
27 refresh contents of top applied patch qrefresh
28
28
29 By default, mq will automatically use git patches when required to
29 By default, mq will automatically use git patches when required to
30 avoid losing file mode changes, copy records, binary files or empty
30 avoid losing file mode changes, copy records, binary files or empty
31 files creations or deletions. This behavior can be configured with::
31 files creations or deletions. This behavior can be configured with::
32
32
33 [mq]
33 [mq]
34 git = auto/keep/yes/no
34 git = auto/keep/yes/no
35
35
36 If set to 'keep', mq will obey the [diff] section configuration while
36 If set to 'keep', mq will obey the [diff] section configuration while
37 preserving existing git patches upon qrefresh. If set to 'yes' or
37 preserving existing git patches upon qrefresh. If set to 'yes' or
38 'no', mq will override the [diff] section and always generate git or
38 'no', mq will override the [diff] section and always generate git or
39 regular patches, possibly losing data in the second case.
39 regular patches, possibly losing data in the second case.
40
40
41 It may be desirable for mq changesets to be kept in the secret phase (see
41 It may be desirable for mq changesets to be kept in the secret phase (see
42 :hg:`help phases`), which can be enabled with the following setting::
42 :hg:`help phases`), which can be enabled with the following setting::
43
43
44 [mq]
44 [mq]
45 secret = True
45 secret = True
46
46
47 You will by default be managing a patch queue named "patches". You can
47 You will by default be managing a patch queue named "patches". You can
48 create other, independent patch queues with the :hg:`qqueue` command.
48 create other, independent patch queues with the :hg:`qqueue` command.
49
49
50 If the working directory contains uncommitted files, qpush, qpop and
50 If the working directory contains uncommitted files, qpush, qpop and
51 qgoto abort immediately. If -f/--force is used, the changes are
51 qgoto abort immediately. If -f/--force is used, the changes are
52 discarded. Setting::
52 discarded. Setting::
53
53
54 [mq]
54 [mq]
55 keepchanges = True
55 keepchanges = True
56
56
57 make them behave as if --keep-changes were passed, and non-conflicting
57 make them behave as if --keep-changes were passed, and non-conflicting
58 local changes will be tolerated and preserved. If incompatible options
58 local changes will be tolerated and preserved. If incompatible options
59 such as -f/--force or --exact are passed, this setting is ignored.
59 such as -f/--force or --exact are passed, this setting is ignored.
60
60
61 This extension used to provide a strip command. This command now lives
61 This extension used to provide a strip command. This command now lives
62 in the strip extension.
62 in the strip extension.
63 '''
63 '''
64
64
65 from __future__ import absolute_import, print_function
65 from __future__ import absolute_import, print_function
66
66
67 import errno
67 import errno
68 import os
68 import os
69 import re
69 import re
70 import shutil
70 import shutil
71 from mercurial.i18n import _
71 from mercurial.i18n import _
72 from mercurial.node import (
72 from mercurial.node import (
73 bin,
73 bin,
74 hex,
74 hex,
75 nullid,
75 nullid,
76 nullrev,
76 nullrev,
77 short,
77 short,
78 )
78 )
79 from mercurial import (
79 from mercurial import (
80 cmdutil,
80 cmdutil,
81 commands,
81 commands,
82 dirstateguard,
82 dirstateguard,
83 encoding,
83 encoding,
84 error,
84 error,
85 extensions,
85 extensions,
86 hg,
86 hg,
87 localrepo,
87 localrepo,
88 lock as lockmod,
88 lock as lockmod,
89 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, pycompat.byterepr(exactneg[0])
664 return False, pycompat.byterepr(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, pycompat.byterepr(exactpos[0])
669 return True, pycompat.byterepr(exactpos[0])
670 return False, ' '.join([pycompat.byterepr(p) for p in pos])
670 return False, ' '.join([pycompat.byterepr(p) for p in 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, bytes):
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 sortedseries = []
1026 reverse=True):
1026 for p in patches:
1027 if i is not None:
1027 idx = self.findseries(p)
1028 if idx is None:
1029 sortedseries.append((-1, p))
1030 else:
1031 sortedseries.append((idx, p))
1032
1033 sortedseries.sort(reverse=True)
1034 for (i, p) in sortedseries:
1035 if i != -1:
1028 del self.fullseries[i]
1036 del self.fullseries[i]
1029 else:
1037 else:
1030 unknown.append(p)
1038 unknown.append(p)
1031
1039
1032 if unknown:
1040 if unknown:
1033 if numrevs:
1041 if numrevs:
1034 rev = dict((entry.name, entry.node) for entry in qfinished)
1042 rev = dict((entry.name, entry.node) for entry in qfinished)
1035 for p in unknown:
1043 for p in unknown:
1036 msg = _('revision %s refers to unknown patches: %s\n')
1044 msg = _('revision %s refers to unknown patches: %s\n')
1037 self.ui.warn(msg % (short(rev[p]), p))
1045 self.ui.warn(msg % (short(rev[p]), p))
1038 else:
1046 else:
1039 msg = _('unknown patches: %s\n')
1047 msg = _('unknown patches: %s\n')
1040 raise error.Abort(''.join(msg % p for p in unknown))
1048 raise error.Abort(''.join(msg % p for p in unknown))
1041
1049
1042 self.parseseries()
1050 self.parseseries()
1043 self.seriesdirty = True
1051 self.seriesdirty = True
1044 return [entry.node for entry in qfinished]
1052 return [entry.node for entry in qfinished]
1045
1053
1046 def _revpatches(self, repo, revs):
1054 def _revpatches(self, repo, revs):
1047 firstrev = repo[self.applied[0].node].rev()
1055 firstrev = repo[self.applied[0].node].rev()
1048 patches = []
1056 patches = []
1049 for i, rev in enumerate(revs):
1057 for i, rev in enumerate(revs):
1050
1058
1051 if rev < firstrev:
1059 if rev < firstrev:
1052 raise error.Abort(_('revision %d is not managed') % rev)
1060 raise error.Abort(_('revision %d is not managed') % rev)
1053
1061
1054 ctx = repo[rev]
1062 ctx = repo[rev]
1055 base = self.applied[i].node
1063 base = self.applied[i].node
1056 if ctx.node() != base:
1064 if ctx.node() != base:
1057 msg = _('cannot delete revision %d above applied patches')
1065 msg = _('cannot delete revision %d above applied patches')
1058 raise error.Abort(msg % rev)
1066 raise error.Abort(msg % rev)
1059
1067
1060 patch = self.applied[i].name
1068 patch = self.applied[i].name
1061 for fmt in ('[mq]: %s', 'imported patch %s'):
1069 for fmt in ('[mq]: %s', 'imported patch %s'):
1062 if ctx.description() == fmt % patch:
1070 if ctx.description() == fmt % patch:
1063 msg = _('patch %s finalized without changeset message\n')
1071 msg = _('patch %s finalized without changeset message\n')
1064 repo.ui.status(msg % patch)
1072 repo.ui.status(msg % patch)
1065 break
1073 break
1066
1074
1067 patches.append(patch)
1075 patches.append(patch)
1068 return patches
1076 return patches
1069
1077
1070 def finish(self, repo, revs):
1078 def finish(self, repo, revs):
1071 # Manually trigger phase computation to ensure phasedefaults is
1079 # Manually trigger phase computation to ensure phasedefaults is
1072 # executed before we remove the patches.
1080 # executed before we remove the patches.
1073 repo._phasecache
1081 repo._phasecache
1074 patches = self._revpatches(repo, sorted(revs))
1082 patches = self._revpatches(repo, sorted(revs))
1075 qfinished = self._cleanup(patches, len(patches))
1083 qfinished = self._cleanup(patches, len(patches))
1076 if qfinished and repo.ui.configbool('mq', 'secret'):
1084 if qfinished and repo.ui.configbool('mq', 'secret'):
1077 # only use this logic when the secret option is added
1085 # only use this logic when the secret option is added
1078 oldqbase = repo[qfinished[0]]
1086 oldqbase = repo[qfinished[0]]
1079 tphase = phases.newcommitphase(repo.ui)
1087 tphase = phases.newcommitphase(repo.ui)
1080 if oldqbase.phase() > tphase and oldqbase.p1().phase() <= tphase:
1088 if oldqbase.phase() > tphase and oldqbase.p1().phase() <= tphase:
1081 with repo.transaction('qfinish') as tr:
1089 with repo.transaction('qfinish') as tr:
1082 phases.advanceboundary(repo, tr, tphase, qfinished)
1090 phases.advanceboundary(repo, tr, tphase, qfinished)
1083
1091
1084 def delete(self, repo, patches, opts):
1092 def delete(self, repo, patches, opts):
1085 if not patches and not opts.get('rev'):
1093 if not patches and not opts.get('rev'):
1086 raise error.Abort(_('qdelete requires at least one revision or '
1094 raise error.Abort(_('qdelete requires at least one revision or '
1087 'patch name'))
1095 'patch name'))
1088
1096
1089 realpatches = []
1097 realpatches = []
1090 for patch in patches:
1098 for patch in patches:
1091 patch = self.lookup(patch, strict=True)
1099 patch = self.lookup(patch, strict=True)
1092 info = self.isapplied(patch)
1100 info = self.isapplied(patch)
1093 if info:
1101 if info:
1094 raise error.Abort(_("cannot delete applied patch %s") % patch)
1102 raise error.Abort(_("cannot delete applied patch %s") % patch)
1095 if patch not in self.series:
1103 if patch not in self.series:
1096 raise error.Abort(_("patch %s not in series file") % patch)
1104 raise error.Abort(_("patch %s not in series file") % patch)
1097 if patch not in realpatches:
1105 if patch not in realpatches:
1098 realpatches.append(patch)
1106 realpatches.append(patch)
1099
1107
1100 numrevs = 0
1108 numrevs = 0
1101 if opts.get('rev'):
1109 if opts.get('rev'):
1102 if not self.applied:
1110 if not self.applied:
1103 raise error.Abort(_('no patches applied'))
1111 raise error.Abort(_('no patches applied'))
1104 revs = scmutil.revrange(repo, opts.get('rev'))
1112 revs = scmutil.revrange(repo, opts.get('rev'))
1105 revs.sort()
1113 revs.sort()
1106 revpatches = self._revpatches(repo, revs)
1114 revpatches = self._revpatches(repo, revs)
1107 realpatches += revpatches
1115 realpatches += revpatches
1108 numrevs = len(revpatches)
1116 numrevs = len(revpatches)
1109
1117
1110 self._cleanup(realpatches, numrevs, opts.get('keep'))
1118 self._cleanup(realpatches, numrevs, opts.get('keep'))
1111
1119
1112 def checktoppatch(self, repo):
1120 def checktoppatch(self, repo):
1113 '''check that working directory is at qtip'''
1121 '''check that working directory is at qtip'''
1114 if self.applied:
1122 if self.applied:
1115 top = self.applied[-1].node
1123 top = self.applied[-1].node
1116 patch = self.applied[-1].name
1124 patch = self.applied[-1].name
1117 if repo.dirstate.p1() != top:
1125 if repo.dirstate.p1() != top:
1118 raise error.Abort(_("working directory revision is not qtip"))
1126 raise error.Abort(_("working directory revision is not qtip"))
1119 return top, patch
1127 return top, patch
1120 return None, None
1128 return None, None
1121
1129
1122 def putsubstate2changes(self, substatestate, changes):
1130 def putsubstate2changes(self, substatestate, changes):
1123 for files in changes[:3]:
1131 for files in changes[:3]:
1124 if '.hgsubstate' in files:
1132 if '.hgsubstate' in files:
1125 return # already listed up
1133 return # already listed up
1126 # not yet listed up
1134 # not yet listed up
1127 if substatestate in 'a?':
1135 if substatestate in 'a?':
1128 changes[1].append('.hgsubstate')
1136 changes[1].append('.hgsubstate')
1129 elif substatestate in 'r':
1137 elif substatestate in 'r':
1130 changes[2].append('.hgsubstate')
1138 changes[2].append('.hgsubstate')
1131 else: # modified
1139 else: # modified
1132 changes[0].append('.hgsubstate')
1140 changes[0].append('.hgsubstate')
1133
1141
1134 def checklocalchanges(self, repo, force=False, refresh=True):
1142 def checklocalchanges(self, repo, force=False, refresh=True):
1135 excsuffix = ''
1143 excsuffix = ''
1136 if refresh:
1144 if refresh:
1137 excsuffix = ', qrefresh first'
1145 excsuffix = ', qrefresh first'
1138 # plain versions for i18n tool to detect them
1146 # plain versions for i18n tool to detect them
1139 _("local changes found, qrefresh first")
1147 _("local changes found, qrefresh first")
1140 _("local changed subrepos found, qrefresh first")
1148 _("local changed subrepos found, qrefresh first")
1141 return checklocalchanges(repo, force, excsuffix)
1149 return checklocalchanges(repo, force, excsuffix)
1142
1150
1143 _reserved = ('series', 'status', 'guards', '.', '..')
1151 _reserved = ('series', 'status', 'guards', '.', '..')
1144 def checkreservedname(self, name):
1152 def checkreservedname(self, name):
1145 if name in self._reserved:
1153 if name in self._reserved:
1146 raise error.Abort(_('"%s" cannot be used as the name of a patch')
1154 raise error.Abort(_('"%s" cannot be used as the name of a patch')
1147 % name)
1155 % name)
1148 if name != name.strip():
1156 if name != name.strip():
1149 # whitespace is stripped by parseseries()
1157 # whitespace is stripped by parseseries()
1150 raise error.Abort(_('patch name cannot begin or end with '
1158 raise error.Abort(_('patch name cannot begin or end with '
1151 'whitespace'))
1159 'whitespace'))
1152 for prefix in ('.hg', '.mq'):
1160 for prefix in ('.hg', '.mq'):
1153 if name.startswith(prefix):
1161 if name.startswith(prefix):
1154 raise error.Abort(_('patch name cannot begin with "%s"')
1162 raise error.Abort(_('patch name cannot begin with "%s"')
1155 % prefix)
1163 % prefix)
1156 for c in ('#', ':', '\r', '\n'):
1164 for c in ('#', ':', '\r', '\n'):
1157 if c in name:
1165 if c in name:
1158 raise error.Abort(_('%r cannot be used in the name of a patch')
1166 raise error.Abort(_('%r cannot be used in the name of a patch')
1159 % c)
1167 % c)
1160
1168
1161 def checkpatchname(self, name, force=False):
1169 def checkpatchname(self, name, force=False):
1162 self.checkreservedname(name)
1170 self.checkreservedname(name)
1163 if not force and os.path.exists(self.join(name)):
1171 if not force and os.path.exists(self.join(name)):
1164 if os.path.isdir(self.join(name)):
1172 if os.path.isdir(self.join(name)):
1165 raise error.Abort(_('"%s" already exists as a directory')
1173 raise error.Abort(_('"%s" already exists as a directory')
1166 % name)
1174 % name)
1167 else:
1175 else:
1168 raise error.Abort(_('patch "%s" already exists') % name)
1176 raise error.Abort(_('patch "%s" already exists') % name)
1169
1177
1170 def makepatchname(self, title, fallbackname):
1178 def makepatchname(self, title, fallbackname):
1171 """Return a suitable filename for title, adding a suffix to make
1179 """Return a suitable filename for title, adding a suffix to make
1172 it unique in the existing list"""
1180 it unique in the existing list"""
1173 namebase = re.sub('[\s\W_]+', '_', title.lower()).strip('_')
1181 namebase = re.sub('[\s\W_]+', '_', title.lower()).strip('_')
1174 namebase = namebase[:75] # avoid too long name (issue5117)
1182 namebase = namebase[:75] # avoid too long name (issue5117)
1175 if namebase:
1183 if namebase:
1176 try:
1184 try:
1177 self.checkreservedname(namebase)
1185 self.checkreservedname(namebase)
1178 except error.Abort:
1186 except error.Abort:
1179 namebase = fallbackname
1187 namebase = fallbackname
1180 else:
1188 else:
1181 namebase = fallbackname
1189 namebase = fallbackname
1182 name = namebase
1190 name = namebase
1183 i = 0
1191 i = 0
1184 while True:
1192 while True:
1185 if name not in self.fullseries:
1193 if name not in self.fullseries:
1186 try:
1194 try:
1187 self.checkpatchname(name)
1195 self.checkpatchname(name)
1188 break
1196 break
1189 except error.Abort:
1197 except error.Abort:
1190 pass
1198 pass
1191 i += 1
1199 i += 1
1192 name = '%s__%d' % (namebase, i)
1200 name = '%s__%d' % (namebase, i)
1193 return name
1201 return name
1194
1202
1195 def checkkeepchanges(self, keepchanges, force):
1203 def checkkeepchanges(self, keepchanges, force):
1196 if force and keepchanges:
1204 if force and keepchanges:
1197 raise error.Abort(_('cannot use both --force and --keep-changes'))
1205 raise error.Abort(_('cannot use both --force and --keep-changes'))
1198
1206
1199 def new(self, repo, patchfn, *pats, **opts):
1207 def new(self, repo, patchfn, *pats, **opts):
1200 """options:
1208 """options:
1201 msg: a string or a no-argument function returning a string
1209 msg: a string or a no-argument function returning a string
1202 """
1210 """
1203 opts = pycompat.byteskwargs(opts)
1211 opts = pycompat.byteskwargs(opts)
1204 msg = opts.get('msg')
1212 msg = opts.get('msg')
1205 edit = opts.get('edit')
1213 edit = opts.get('edit')
1206 editform = opts.get('editform', 'mq.qnew')
1214 editform = opts.get('editform', 'mq.qnew')
1207 user = opts.get('user')
1215 user = opts.get('user')
1208 date = opts.get('date')
1216 date = opts.get('date')
1209 if date:
1217 if date:
1210 date = dateutil.parsedate(date)
1218 date = dateutil.parsedate(date)
1211 diffopts = self.diffopts({'git': opts.get('git')}, plain=True)
1219 diffopts = self.diffopts({'git': opts.get('git')}, plain=True)
1212 if opts.get('checkname', True):
1220 if opts.get('checkname', True):
1213 self.checkpatchname(patchfn)
1221 self.checkpatchname(patchfn)
1214 inclsubs = checksubstate(repo)
1222 inclsubs = checksubstate(repo)
1215 if inclsubs:
1223 if inclsubs:
1216 substatestate = repo.dirstate['.hgsubstate']
1224 substatestate = repo.dirstate['.hgsubstate']
1217 if opts.get('include') or opts.get('exclude') or pats:
1225 if opts.get('include') or opts.get('exclude') or pats:
1218 # detect missing files in pats
1226 # detect missing files in pats
1219 def badfn(f, msg):
1227 def badfn(f, msg):
1220 if f != '.hgsubstate': # .hgsubstate is auto-created
1228 if f != '.hgsubstate': # .hgsubstate is auto-created
1221 raise error.Abort('%s: %s' % (f, msg))
1229 raise error.Abort('%s: %s' % (f, msg))
1222 match = scmutil.match(repo[None], pats, opts, badfn=badfn)
1230 match = scmutil.match(repo[None], pats, opts, badfn=badfn)
1223 changes = repo.status(match=match)
1231 changes = repo.status(match=match)
1224 else:
1232 else:
1225 changes = self.checklocalchanges(repo, force=True)
1233 changes = self.checklocalchanges(repo, force=True)
1226 commitfiles = list(inclsubs)
1234 commitfiles = list(inclsubs)
1227 for files in changes[:3]:
1235 for files in changes[:3]:
1228 commitfiles.extend(files)
1236 commitfiles.extend(files)
1229 match = scmutil.matchfiles(repo, commitfiles)
1237 match = scmutil.matchfiles(repo, commitfiles)
1230 if len(repo[None].parents()) > 1:
1238 if len(repo[None].parents()) > 1:
1231 raise error.Abort(_('cannot manage merge changesets'))
1239 raise error.Abort(_('cannot manage merge changesets'))
1232 self.checktoppatch(repo)
1240 self.checktoppatch(repo)
1233 insert = self.fullseriesend()
1241 insert = self.fullseriesend()
1234 with repo.wlock():
1242 with repo.wlock():
1235 try:
1243 try:
1236 # if patch file write fails, abort early
1244 # if patch file write fails, abort early
1237 p = self.opener(patchfn, "w")
1245 p = self.opener(patchfn, "w")
1238 except IOError as e:
1246 except IOError as e:
1239 raise error.Abort(_('cannot write patch "%s": %s')
1247 raise error.Abort(_('cannot write patch "%s": %s')
1240 % (patchfn, encoding.strtolocal(e.strerror)))
1248 % (patchfn, encoding.strtolocal(e.strerror)))
1241 try:
1249 try:
1242 defaultmsg = "[mq]: %s" % patchfn
1250 defaultmsg = "[mq]: %s" % patchfn
1243 editor = cmdutil.getcommiteditor(editform=editform)
1251 editor = cmdutil.getcommiteditor(editform=editform)
1244 if edit:
1252 if edit:
1245 def finishdesc(desc):
1253 def finishdesc(desc):
1246 if desc.rstrip():
1254 if desc.rstrip():
1247 return desc
1255 return desc
1248 else:
1256 else:
1249 return defaultmsg
1257 return defaultmsg
1250 # i18n: this message is shown in editor with "HG: " prefix
1258 # i18n: this message is shown in editor with "HG: " prefix
1251 extramsg = _('Leave message empty to use default message.')
1259 extramsg = _('Leave message empty to use default message.')
1252 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1260 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1253 extramsg=extramsg,
1261 extramsg=extramsg,
1254 editform=editform)
1262 editform=editform)
1255 commitmsg = msg
1263 commitmsg = msg
1256 else:
1264 else:
1257 commitmsg = msg or defaultmsg
1265 commitmsg = msg or defaultmsg
1258
1266
1259 n = newcommit(repo, None, commitmsg, user, date, match=match,
1267 n = newcommit(repo, None, commitmsg, user, date, match=match,
1260 force=True, editor=editor)
1268 force=True, editor=editor)
1261 if n is None:
1269 if n is None:
1262 raise error.Abort(_("repo commit failed"))
1270 raise error.Abort(_("repo commit failed"))
1263 try:
1271 try:
1264 self.fullseries[insert:insert] = [patchfn]
1272 self.fullseries[insert:insert] = [patchfn]
1265 self.applied.append(statusentry(n, patchfn))
1273 self.applied.append(statusentry(n, patchfn))
1266 self.parseseries()
1274 self.parseseries()
1267 self.seriesdirty = True
1275 self.seriesdirty = True
1268 self.applieddirty = True
1276 self.applieddirty = True
1269 nctx = repo[n]
1277 nctx = repo[n]
1270 ph = patchheader(self.join(patchfn), self.plainmode)
1278 ph = patchheader(self.join(patchfn), self.plainmode)
1271 if user:
1279 if user:
1272 ph.setuser(user)
1280 ph.setuser(user)
1273 if date:
1281 if date:
1274 ph.setdate('%d %d' % date)
1282 ph.setdate('%d %d' % date)
1275 ph.setparent(hex(nctx.p1().node()))
1283 ph.setparent(hex(nctx.p1().node()))
1276 msg = nctx.description().strip()
1284 msg = nctx.description().strip()
1277 if msg == defaultmsg.strip():
1285 if msg == defaultmsg.strip():
1278 msg = ''
1286 msg = ''
1279 ph.setmessage(msg)
1287 ph.setmessage(msg)
1280 p.write(bytes(ph))
1288 p.write(bytes(ph))
1281 if commitfiles:
1289 if commitfiles:
1282 parent = self.qparents(repo, n)
1290 parent = self.qparents(repo, n)
1283 if inclsubs:
1291 if inclsubs:
1284 self.putsubstate2changes(substatestate, changes)
1292 self.putsubstate2changes(substatestate, changes)
1285 chunks = patchmod.diff(repo, node1=parent, node2=n,
1293 chunks = patchmod.diff(repo, node1=parent, node2=n,
1286 changes=changes, opts=diffopts)
1294 changes=changes, opts=diffopts)
1287 for chunk in chunks:
1295 for chunk in chunks:
1288 p.write(chunk)
1296 p.write(chunk)
1289 p.close()
1297 p.close()
1290 r = self.qrepo()
1298 r = self.qrepo()
1291 if r:
1299 if r:
1292 r[None].add([patchfn])
1300 r[None].add([patchfn])
1293 except: # re-raises
1301 except: # re-raises
1294 repo.rollback()
1302 repo.rollback()
1295 raise
1303 raise
1296 except Exception:
1304 except Exception:
1297 patchpath = self.join(patchfn)
1305 patchpath = self.join(patchfn)
1298 try:
1306 try:
1299 os.unlink(patchpath)
1307 os.unlink(patchpath)
1300 except OSError:
1308 except OSError:
1301 self.ui.warn(_('error unlinking %s\n') % patchpath)
1309 self.ui.warn(_('error unlinking %s\n') % patchpath)
1302 raise
1310 raise
1303 self.removeundo(repo)
1311 self.removeundo(repo)
1304
1312
1305 def isapplied(self, patch):
1313 def isapplied(self, patch):
1306 """returns (index, rev, patch)"""
1314 """returns (index, rev, patch)"""
1307 for i, a in enumerate(self.applied):
1315 for i, a in enumerate(self.applied):
1308 if a.name == patch:
1316 if a.name == patch:
1309 return (i, a.node, a.name)
1317 return (i, a.node, a.name)
1310 return None
1318 return None
1311
1319
1312 # if the exact patch name does not exist, we try a few
1320 # if the exact patch name does not exist, we try a few
1313 # variations. If strict is passed, we try only #1
1321 # variations. If strict is passed, we try only #1
1314 #
1322 #
1315 # 1) a number (as string) to indicate an offset in the series file
1323 # 1) a number (as string) to indicate an offset in the series file
1316 # 2) a unique substring of the patch name was given
1324 # 2) a unique substring of the patch name was given
1317 # 3) patchname[-+]num to indicate an offset in the series file
1325 # 3) patchname[-+]num to indicate an offset in the series file
1318 def lookup(self, patch, strict=False):
1326 def lookup(self, patch, strict=False):
1319 def partialname(s):
1327 def partialname(s):
1320 if s in self.series:
1328 if s in self.series:
1321 return s
1329 return s
1322 matches = [x for x in self.series if s in x]
1330 matches = [x for x in self.series if s in x]
1323 if len(matches) > 1:
1331 if len(matches) > 1:
1324 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1332 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1325 for m in matches:
1333 for m in matches:
1326 self.ui.warn(' %s\n' % m)
1334 self.ui.warn(' %s\n' % m)
1327 return None
1335 return None
1328 if matches:
1336 if matches:
1329 return matches[0]
1337 return matches[0]
1330 if self.series and self.applied:
1338 if self.series and self.applied:
1331 if s == 'qtip':
1339 if s == 'qtip':
1332 return self.series[self.seriesend(True) - 1]
1340 return self.series[self.seriesend(True) - 1]
1333 if s == 'qbase':
1341 if s == 'qbase':
1334 return self.series[0]
1342 return self.series[0]
1335 return None
1343 return None
1336
1344
1337 if patch in self.series:
1345 if patch in self.series:
1338 return patch
1346 return patch
1339
1347
1340 if not os.path.isfile(self.join(patch)):
1348 if not os.path.isfile(self.join(patch)):
1341 try:
1349 try:
1342 sno = int(patch)
1350 sno = int(patch)
1343 except (ValueError, OverflowError):
1351 except (ValueError, OverflowError):
1344 pass
1352 pass
1345 else:
1353 else:
1346 if -len(self.series) <= sno < len(self.series):
1354 if -len(self.series) <= sno < len(self.series):
1347 return self.series[sno]
1355 return self.series[sno]
1348
1356
1349 if not strict:
1357 if not strict:
1350 res = partialname(patch)
1358 res = partialname(patch)
1351 if res:
1359 if res:
1352 return res
1360 return res
1353 minus = patch.rfind('-')
1361 minus = patch.rfind('-')
1354 if minus >= 0:
1362 if minus >= 0:
1355 res = partialname(patch[:minus])
1363 res = partialname(patch[:minus])
1356 if res:
1364 if res:
1357 i = self.series.index(res)
1365 i = self.series.index(res)
1358 try:
1366 try:
1359 off = int(patch[minus + 1:] or 1)
1367 off = int(patch[minus + 1:] or 1)
1360 except (ValueError, OverflowError):
1368 except (ValueError, OverflowError):
1361 pass
1369 pass
1362 else:
1370 else:
1363 if i - off >= 0:
1371 if i - off >= 0:
1364 return self.series[i - off]
1372 return self.series[i - off]
1365 plus = patch.rfind('+')
1373 plus = patch.rfind('+')
1366 if plus >= 0:
1374 if plus >= 0:
1367 res = partialname(patch[:plus])
1375 res = partialname(patch[:plus])
1368 if res:
1376 if res:
1369 i = self.series.index(res)
1377 i = self.series.index(res)
1370 try:
1378 try:
1371 off = int(patch[plus + 1:] or 1)
1379 off = int(patch[plus + 1:] or 1)
1372 except (ValueError, OverflowError):
1380 except (ValueError, OverflowError):
1373 pass
1381 pass
1374 else:
1382 else:
1375 if i + off < len(self.series):
1383 if i + off < len(self.series):
1376 return self.series[i + off]
1384 return self.series[i + off]
1377 raise error.Abort(_("patch %s not in series") % patch)
1385 raise error.Abort(_("patch %s not in series") % patch)
1378
1386
1379 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1387 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1380 all=False, move=False, exact=False, nobackup=False,
1388 all=False, move=False, exact=False, nobackup=False,
1381 keepchanges=False):
1389 keepchanges=False):
1382 self.checkkeepchanges(keepchanges, force)
1390 self.checkkeepchanges(keepchanges, force)
1383 diffopts = self.diffopts()
1391 diffopts = self.diffopts()
1384 with repo.wlock():
1392 with repo.wlock():
1385 heads = []
1393 heads = []
1386 for hs in repo.branchmap().itervalues():
1394 for hs in repo.branchmap().itervalues():
1387 heads.extend(hs)
1395 heads.extend(hs)
1388 if not heads:
1396 if not heads:
1389 heads = [nullid]
1397 heads = [nullid]
1390 if repo.dirstate.p1() not in heads and not exact:
1398 if repo.dirstate.p1() not in heads and not exact:
1391 self.ui.status(_("(working directory not at a head)\n"))
1399 self.ui.status(_("(working directory not at a head)\n"))
1392
1400
1393 if not self.series:
1401 if not self.series:
1394 self.ui.warn(_('no patches in series\n'))
1402 self.ui.warn(_('no patches in series\n'))
1395 return 0
1403 return 0
1396
1404
1397 # Suppose our series file is: A B C and the current 'top'
1405 # Suppose our series file is: A B C and the current 'top'
1398 # patch is B. qpush C should be performed (moving forward)
1406 # 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
1407 # qpush B is a NOP (no change) qpush A is an error (can't
1400 # go backwards with qpush)
1408 # go backwards with qpush)
1401 if patch:
1409 if patch:
1402 patch = self.lookup(patch)
1410 patch = self.lookup(patch)
1403 info = self.isapplied(patch)
1411 info = self.isapplied(patch)
1404 if info and info[0] >= len(self.applied) - 1:
1412 if info and info[0] >= len(self.applied) - 1:
1405 self.ui.warn(
1413 self.ui.warn(
1406 _('qpush: %s is already at the top\n') % patch)
1414 _('qpush: %s is already at the top\n') % patch)
1407 return 0
1415 return 0
1408
1416
1409 pushable, reason = self.pushable(patch)
1417 pushable, reason = self.pushable(patch)
1410 if pushable:
1418 if pushable:
1411 if self.series.index(patch) < self.seriesend():
1419 if self.series.index(patch) < self.seriesend():
1412 raise error.Abort(
1420 raise error.Abort(
1413 _("cannot push to a previous patch: %s") % patch)
1421 _("cannot push to a previous patch: %s") % patch)
1414 else:
1422 else:
1415 if reason:
1423 if reason:
1416 reason = _('guarded by %s') % reason
1424 reason = _('guarded by %s') % reason
1417 else:
1425 else:
1418 reason = _('no matching guards')
1426 reason = _('no matching guards')
1419 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1427 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1420 return 1
1428 return 1
1421 elif all:
1429 elif all:
1422 patch = self.series[-1]
1430 patch = self.series[-1]
1423 if self.isapplied(patch):
1431 if self.isapplied(patch):
1424 self.ui.warn(_('all patches are currently applied\n'))
1432 self.ui.warn(_('all patches are currently applied\n'))
1425 return 0
1433 return 0
1426
1434
1427 # Following the above example, starting at 'top' of B:
1435 # Following the above example, starting at 'top' of B:
1428 # qpush should be performed (pushes C), but a subsequent
1436 # qpush should be performed (pushes C), but a subsequent
1429 # qpush without an argument is an error (nothing to
1437 # qpush without an argument is an error (nothing to
1430 # apply). This allows a loop of "...while hg qpush..." to
1438 # apply). This allows a loop of "...while hg qpush..." to
1431 # work as it detects an error when done
1439 # work as it detects an error when done
1432 start = self.seriesend()
1440 start = self.seriesend()
1433 if start == len(self.series):
1441 if start == len(self.series):
1434 self.ui.warn(_('patch series already fully applied\n'))
1442 self.ui.warn(_('patch series already fully applied\n'))
1435 return 1
1443 return 1
1436 if not force and not keepchanges:
1444 if not force and not keepchanges:
1437 self.checklocalchanges(repo, refresh=self.applied)
1445 self.checklocalchanges(repo, refresh=self.applied)
1438
1446
1439 if exact:
1447 if exact:
1440 if keepchanges:
1448 if keepchanges:
1441 raise error.Abort(
1449 raise error.Abort(
1442 _("cannot use --exact and --keep-changes together"))
1450 _("cannot use --exact and --keep-changes together"))
1443 if move:
1451 if move:
1444 raise error.Abort(_('cannot use --exact and --move '
1452 raise error.Abort(_('cannot use --exact and --move '
1445 'together'))
1453 'together'))
1446 if self.applied:
1454 if self.applied:
1447 raise error.Abort(_('cannot push --exact with applied '
1455 raise error.Abort(_('cannot push --exact with applied '
1448 'patches'))
1456 'patches'))
1449 root = self.series[start]
1457 root = self.series[start]
1450 target = patchheader(self.join(root), self.plainmode).parent
1458 target = patchheader(self.join(root), self.plainmode).parent
1451 if not target:
1459 if not target:
1452 raise error.Abort(
1460 raise error.Abort(
1453 _("%s does not have a parent recorded") % root)
1461 _("%s does not have a parent recorded") % root)
1454 if not repo[target] == repo['.']:
1462 if not repo[target] == repo['.']:
1455 hg.update(repo, target)
1463 hg.update(repo, target)
1456
1464
1457 if move:
1465 if move:
1458 if not patch:
1466 if not patch:
1459 raise error.Abort(_("please specify the patch to move"))
1467 raise error.Abort(_("please specify the patch to move"))
1460 for fullstart, rpn in enumerate(self.fullseries):
1468 for fullstart, rpn in enumerate(self.fullseries):
1461 # strip markers for patch guards
1469 # strip markers for patch guards
1462 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1470 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1463 break
1471 break
1464 for i, rpn in enumerate(self.fullseries[fullstart:]):
1472 for i, rpn in enumerate(self.fullseries[fullstart:]):
1465 # strip markers for patch guards
1473 # strip markers for patch guards
1466 if self.guard_re.split(rpn, 1)[0] == patch:
1474 if self.guard_re.split(rpn, 1)[0] == patch:
1467 break
1475 break
1468 index = fullstart + i
1476 index = fullstart + i
1469 assert index < len(self.fullseries)
1477 assert index < len(self.fullseries)
1470 fullpatch = self.fullseries[index]
1478 fullpatch = self.fullseries[index]
1471 del self.fullseries[index]
1479 del self.fullseries[index]
1472 self.fullseries.insert(fullstart, fullpatch)
1480 self.fullseries.insert(fullstart, fullpatch)
1473 self.parseseries()
1481 self.parseseries()
1474 self.seriesdirty = True
1482 self.seriesdirty = True
1475
1483
1476 self.applieddirty = True
1484 self.applieddirty = True
1477 if start > 0:
1485 if start > 0:
1478 self.checktoppatch(repo)
1486 self.checktoppatch(repo)
1479 if not patch:
1487 if not patch:
1480 patch = self.series[start]
1488 patch = self.series[start]
1481 end = start + 1
1489 end = start + 1
1482 else:
1490 else:
1483 end = self.series.index(patch, start) + 1
1491 end = self.series.index(patch, start) + 1
1484
1492
1485 tobackup = set()
1493 tobackup = set()
1486 if (not nobackup and force) or keepchanges:
1494 if (not nobackup and force) or keepchanges:
1487 status = self.checklocalchanges(repo, force=True)
1495 status = self.checklocalchanges(repo, force=True)
1488 if keepchanges:
1496 if keepchanges:
1489 tobackup.update(status.modified + status.added +
1497 tobackup.update(status.modified + status.added +
1490 status.removed + status.deleted)
1498 status.removed + status.deleted)
1491 else:
1499 else:
1492 tobackup.update(status.modified + status.added)
1500 tobackup.update(status.modified + status.added)
1493
1501
1494 s = self.series[start:end]
1502 s = self.series[start:end]
1495 all_files = set()
1503 all_files = set()
1496 try:
1504 try:
1497 if mergeq:
1505 if mergeq:
1498 ret = self.mergepatch(repo, mergeq, s, diffopts)
1506 ret = self.mergepatch(repo, mergeq, s, diffopts)
1499 else:
1507 else:
1500 ret = self.apply(repo, s, list, all_files=all_files,
1508 ret = self.apply(repo, s, list, all_files=all_files,
1501 tobackup=tobackup, keepchanges=keepchanges)
1509 tobackup=tobackup, keepchanges=keepchanges)
1502 except AbortNoCleanup:
1510 except AbortNoCleanup:
1503 raise
1511 raise
1504 except: # re-raises
1512 except: # re-raises
1505 self.ui.warn(_('cleaning up working directory...\n'))
1513 self.ui.warn(_('cleaning up working directory...\n'))
1506 cmdutil.revert(self.ui, repo, repo['.'],
1514 cmdutil.revert(self.ui, repo, repo['.'],
1507 repo.dirstate.parents(), no_backup=True)
1515 repo.dirstate.parents(), no_backup=True)
1508 # only remove unknown files that we know we touched or
1516 # only remove unknown files that we know we touched or
1509 # created while patching
1517 # created while patching
1510 for f in all_files:
1518 for f in all_files:
1511 if f not in repo.dirstate:
1519 if f not in repo.dirstate:
1512 repo.wvfs.unlinkpath(f, ignoremissing=True)
1520 repo.wvfs.unlinkpath(f, ignoremissing=True)
1513 self.ui.warn(_('done\n'))
1521 self.ui.warn(_('done\n'))
1514 raise
1522 raise
1515
1523
1516 if not self.applied:
1524 if not self.applied:
1517 return ret[0]
1525 return ret[0]
1518 top = self.applied[-1].name
1526 top = self.applied[-1].name
1519 if ret[0] and ret[0] > 1:
1527 if ret[0] and ret[0] > 1:
1520 msg = _("errors during apply, please fix and qrefresh %s\n")
1528 msg = _("errors during apply, please fix and qrefresh %s\n")
1521 self.ui.write(msg % top)
1529 self.ui.write(msg % top)
1522 else:
1530 else:
1523 self.ui.write(_("now at: %s\n") % top)
1531 self.ui.write(_("now at: %s\n") % top)
1524 return ret[0]
1532 return ret[0]
1525
1533
1526 def pop(self, repo, patch=None, force=False, update=True, all=False,
1534 def pop(self, repo, patch=None, force=False, update=True, all=False,
1527 nobackup=False, keepchanges=False):
1535 nobackup=False, keepchanges=False):
1528 self.checkkeepchanges(keepchanges, force)
1536 self.checkkeepchanges(keepchanges, force)
1529 with repo.wlock():
1537 with repo.wlock():
1530 if patch:
1538 if patch:
1531 # index, rev, patch
1539 # index, rev, patch
1532 info = self.isapplied(patch)
1540 info = self.isapplied(patch)
1533 if not info:
1541 if not info:
1534 patch = self.lookup(patch)
1542 patch = self.lookup(patch)
1535 info = self.isapplied(patch)
1543 info = self.isapplied(patch)
1536 if not info:
1544 if not info:
1537 raise error.Abort(_("patch %s is not applied") % patch)
1545 raise error.Abort(_("patch %s is not applied") % patch)
1538
1546
1539 if not self.applied:
1547 if not self.applied:
1540 # Allow qpop -a to work repeatedly,
1548 # Allow qpop -a to work repeatedly,
1541 # but not qpop without an argument
1549 # but not qpop without an argument
1542 self.ui.warn(_("no patches applied\n"))
1550 self.ui.warn(_("no patches applied\n"))
1543 return not all
1551 return not all
1544
1552
1545 if all:
1553 if all:
1546 start = 0
1554 start = 0
1547 elif patch:
1555 elif patch:
1548 start = info[0] + 1
1556 start = info[0] + 1
1549 else:
1557 else:
1550 start = len(self.applied) - 1
1558 start = len(self.applied) - 1
1551
1559
1552 if start >= len(self.applied):
1560 if start >= len(self.applied):
1553 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1561 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1554 return
1562 return
1555
1563
1556 if not update:
1564 if not update:
1557 parents = repo.dirstate.parents()
1565 parents = repo.dirstate.parents()
1558 rr = [x.node for x in self.applied]
1566 rr = [x.node for x in self.applied]
1559 for p in parents:
1567 for p in parents:
1560 if p in rr:
1568 if p in rr:
1561 self.ui.warn(_("qpop: forcing dirstate update\n"))
1569 self.ui.warn(_("qpop: forcing dirstate update\n"))
1562 update = True
1570 update = True
1563 else:
1571 else:
1564 parents = [p.node() for p in repo[None].parents()]
1572 parents = [p.node() for p in repo[None].parents()]
1565 update = any(entry.node in parents
1573 update = any(entry.node in parents
1566 for entry in self.applied[start:])
1574 for entry in self.applied[start:])
1567
1575
1568 tobackup = set()
1576 tobackup = set()
1569 if update:
1577 if update:
1570 s = self.checklocalchanges(repo, force=force or keepchanges)
1578 s = self.checklocalchanges(repo, force=force or keepchanges)
1571 if force:
1579 if force:
1572 if not nobackup:
1580 if not nobackup:
1573 tobackup.update(s.modified + s.added)
1581 tobackup.update(s.modified + s.added)
1574 elif keepchanges:
1582 elif keepchanges:
1575 tobackup.update(s.modified + s.added +
1583 tobackup.update(s.modified + s.added +
1576 s.removed + s.deleted)
1584 s.removed + s.deleted)
1577
1585
1578 self.applieddirty = True
1586 self.applieddirty = True
1579 end = len(self.applied)
1587 end = len(self.applied)
1580 rev = self.applied[start].node
1588 rev = self.applied[start].node
1581
1589
1582 try:
1590 try:
1583 heads = repo.changelog.heads(rev)
1591 heads = repo.changelog.heads(rev)
1584 except error.LookupError:
1592 except error.LookupError:
1585 node = short(rev)
1593 node = short(rev)
1586 raise error.Abort(_('trying to pop unknown node %s') % node)
1594 raise error.Abort(_('trying to pop unknown node %s') % node)
1587
1595
1588 if heads != [self.applied[-1].node]:
1596 if heads != [self.applied[-1].node]:
1589 raise error.Abort(_("popping would remove a revision not "
1597 raise error.Abort(_("popping would remove a revision not "
1590 "managed by this patch queue"))
1598 "managed by this patch queue"))
1591 if not repo[self.applied[-1].node].mutable():
1599 if not repo[self.applied[-1].node].mutable():
1592 raise error.Abort(
1600 raise error.Abort(
1593 _("popping would remove a public revision"),
1601 _("popping would remove a public revision"),
1594 hint=_("see 'hg help phases' for details"))
1602 hint=_("see 'hg help phases' for details"))
1595
1603
1596 # we know there are no local changes, so we can make a simplified
1604 # we know there are no local changes, so we can make a simplified
1597 # form of hg.update.
1605 # form of hg.update.
1598 if update:
1606 if update:
1599 qp = self.qparents(repo, rev)
1607 qp = self.qparents(repo, rev)
1600 ctx = repo[qp]
1608 ctx = repo[qp]
1601 m, a, r, d = repo.status(qp, '.')[:4]
1609 m, a, r, d = repo.status(qp, '.')[:4]
1602 if d:
1610 if d:
1603 raise error.Abort(_("deletions found between repo revs"))
1611 raise error.Abort(_("deletions found between repo revs"))
1604
1612
1605 tobackup = set(a + m + r) & tobackup
1613 tobackup = set(a + m + r) & tobackup
1606 if keepchanges and tobackup:
1614 if keepchanges and tobackup:
1607 raise error.Abort(_("local changes found, qrefresh first"))
1615 raise error.Abort(_("local changes found, qrefresh first"))
1608 self.backup(repo, tobackup)
1616 self.backup(repo, tobackup)
1609 with repo.dirstate.parentchange():
1617 with repo.dirstate.parentchange():
1610 for f in a:
1618 for f in a:
1611 repo.wvfs.unlinkpath(f, ignoremissing=True)
1619 repo.wvfs.unlinkpath(f, ignoremissing=True)
1612 repo.dirstate.drop(f)
1620 repo.dirstate.drop(f)
1613 for f in m + r:
1621 for f in m + r:
1614 fctx = ctx[f]
1622 fctx = ctx[f]
1615 repo.wwrite(f, fctx.data(), fctx.flags())
1623 repo.wwrite(f, fctx.data(), fctx.flags())
1616 repo.dirstate.normal(f)
1624 repo.dirstate.normal(f)
1617 repo.setparents(qp, nullid)
1625 repo.setparents(qp, nullid)
1618 for patch in reversed(self.applied[start:end]):
1626 for patch in reversed(self.applied[start:end]):
1619 self.ui.status(_("popping %s\n") % patch.name)
1627 self.ui.status(_("popping %s\n") % patch.name)
1620 del self.applied[start:end]
1628 del self.applied[start:end]
1621 strip(self.ui, repo, [rev], update=False, backup=False)
1629 strip(self.ui, repo, [rev], update=False, backup=False)
1622 for s, state in repo['.'].substate.items():
1630 for s, state in repo['.'].substate.items():
1623 repo['.'].sub(s).get(state)
1631 repo['.'].sub(s).get(state)
1624 if self.applied:
1632 if self.applied:
1625 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1633 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1626 else:
1634 else:
1627 self.ui.write(_("patch queue now empty\n"))
1635 self.ui.write(_("patch queue now empty\n"))
1628
1636
1629 def diff(self, repo, pats, opts):
1637 def diff(self, repo, pats, opts):
1630 top, patch = self.checktoppatch(repo)
1638 top, patch = self.checktoppatch(repo)
1631 if not top:
1639 if not top:
1632 self.ui.write(_("no patches applied\n"))
1640 self.ui.write(_("no patches applied\n"))
1633 return
1641 return
1634 qp = self.qparents(repo, top)
1642 qp = self.qparents(repo, top)
1635 if opts.get('reverse'):
1643 if opts.get('reverse'):
1636 node1, node2 = None, qp
1644 node1, node2 = None, qp
1637 else:
1645 else:
1638 node1, node2 = qp, None
1646 node1, node2 = qp, None
1639 diffopts = self.diffopts(opts, patch)
1647 diffopts = self.diffopts(opts, patch)
1640 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1648 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1641
1649
1642 def refresh(self, repo, pats=None, **opts):
1650 def refresh(self, repo, pats=None, **opts):
1643 opts = pycompat.byteskwargs(opts)
1651 opts = pycompat.byteskwargs(opts)
1644 if not self.applied:
1652 if not self.applied:
1645 self.ui.write(_("no patches applied\n"))
1653 self.ui.write(_("no patches applied\n"))
1646 return 1
1654 return 1
1647 msg = opts.get('msg', '').rstrip()
1655 msg = opts.get('msg', '').rstrip()
1648 edit = opts.get('edit')
1656 edit = opts.get('edit')
1649 editform = opts.get('editform', 'mq.qrefresh')
1657 editform = opts.get('editform', 'mq.qrefresh')
1650 newuser = opts.get('user')
1658 newuser = opts.get('user')
1651 newdate = opts.get('date')
1659 newdate = opts.get('date')
1652 if newdate:
1660 if newdate:
1653 newdate = '%d %d' % dateutil.parsedate(newdate)
1661 newdate = '%d %d' % dateutil.parsedate(newdate)
1654 wlock = repo.wlock()
1662 wlock = repo.wlock()
1655
1663
1656 try:
1664 try:
1657 self.checktoppatch(repo)
1665 self.checktoppatch(repo)
1658 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1666 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1659 if repo.changelog.heads(top) != [top]:
1667 if repo.changelog.heads(top) != [top]:
1660 raise error.Abort(_("cannot qrefresh a revision with children"))
1668 raise error.Abort(_("cannot qrefresh a revision with children"))
1661 if not repo[top].mutable():
1669 if not repo[top].mutable():
1662 raise error.Abort(_("cannot qrefresh public revision"),
1670 raise error.Abort(_("cannot qrefresh public revision"),
1663 hint=_("see 'hg help phases' for details"))
1671 hint=_("see 'hg help phases' for details"))
1664
1672
1665 cparents = repo.changelog.parents(top)
1673 cparents = repo.changelog.parents(top)
1666 patchparent = self.qparents(repo, top)
1674 patchparent = self.qparents(repo, top)
1667
1675
1668 inclsubs = checksubstate(repo, patchparent)
1676 inclsubs = checksubstate(repo, patchparent)
1669 if inclsubs:
1677 if inclsubs:
1670 substatestate = repo.dirstate['.hgsubstate']
1678 substatestate = repo.dirstate['.hgsubstate']
1671
1679
1672 ph = patchheader(self.join(patchfn), self.plainmode)
1680 ph = patchheader(self.join(patchfn), self.plainmode)
1673 diffopts = self.diffopts({'git': opts.get('git')}, patchfn,
1681 diffopts = self.diffopts({'git': opts.get('git')}, patchfn,
1674 plain=True)
1682 plain=True)
1675 if newuser:
1683 if newuser:
1676 ph.setuser(newuser)
1684 ph.setuser(newuser)
1677 if newdate:
1685 if newdate:
1678 ph.setdate(newdate)
1686 ph.setdate(newdate)
1679 ph.setparent(hex(patchparent))
1687 ph.setparent(hex(patchparent))
1680
1688
1681 # only commit new patch when write is complete
1689 # only commit new patch when write is complete
1682 patchf = self.opener(patchfn, 'w', atomictemp=True)
1690 patchf = self.opener(patchfn, 'w', atomictemp=True)
1683
1691
1684 # update the dirstate in place, strip off the qtip commit
1692 # update the dirstate in place, strip off the qtip commit
1685 # and then commit.
1693 # and then commit.
1686 #
1694 #
1687 # this should really read:
1695 # this should really read:
1688 # mm, dd, aa = repo.status(top, patchparent)[:3]
1696 # mm, dd, aa = repo.status(top, patchparent)[:3]
1689 # but we do it backwards to take advantage of manifest/changelog
1697 # but we do it backwards to take advantage of manifest/changelog
1690 # caching against the next repo.status call
1698 # caching against the next repo.status call
1691 mm, aa, dd = repo.status(patchparent, top)[:3]
1699 mm, aa, dd = repo.status(patchparent, top)[:3]
1692 changes = repo.changelog.read(top)
1700 changes = repo.changelog.read(top)
1693 man = repo.manifestlog[changes[0]].read()
1701 man = repo.manifestlog[changes[0]].read()
1694 aaa = aa[:]
1702 aaa = aa[:]
1695 match1 = scmutil.match(repo[None], pats, opts)
1703 match1 = scmutil.match(repo[None], pats, opts)
1696 # in short mode, we only diff the files included in the
1704 # in short mode, we only diff the files included in the
1697 # patch already plus specified files
1705 # patch already plus specified files
1698 if opts.get('short'):
1706 if opts.get('short'):
1699 # if amending a patch, we start with existing
1707 # if amending a patch, we start with existing
1700 # files plus specified files - unfiltered
1708 # files plus specified files - unfiltered
1701 match = scmutil.matchfiles(repo, mm + aa + dd + match1.files())
1709 match = scmutil.matchfiles(repo, mm + aa + dd + match1.files())
1702 # filter with include/exclude options
1710 # filter with include/exclude options
1703 match1 = scmutil.match(repo[None], opts=opts)
1711 match1 = scmutil.match(repo[None], opts=opts)
1704 else:
1712 else:
1705 match = scmutil.matchall(repo)
1713 match = scmutil.matchall(repo)
1706 m, a, r, d = repo.status(match=match)[:4]
1714 m, a, r, d = repo.status(match=match)[:4]
1707 mm = set(mm)
1715 mm = set(mm)
1708 aa = set(aa)
1716 aa = set(aa)
1709 dd = set(dd)
1717 dd = set(dd)
1710
1718
1711 # we might end up with files that were added between
1719 # we might end up with files that were added between
1712 # qtip and the dirstate parent, but then changed in the
1720 # qtip and the dirstate parent, but then changed in the
1713 # local dirstate. in this case, we want them to only
1721 # local dirstate. in this case, we want them to only
1714 # show up in the added section
1722 # show up in the added section
1715 for x in m:
1723 for x in m:
1716 if x not in aa:
1724 if x not in aa:
1717 mm.add(x)
1725 mm.add(x)
1718 # we might end up with files added by the local dirstate that
1726 # we might end up with files added by the local dirstate that
1719 # were deleted by the patch. In this case, they should only
1727 # were deleted by the patch. In this case, they should only
1720 # show up in the changed section.
1728 # show up in the changed section.
1721 for x in a:
1729 for x in a:
1722 if x in dd:
1730 if x in dd:
1723 dd.remove(x)
1731 dd.remove(x)
1724 mm.add(x)
1732 mm.add(x)
1725 else:
1733 else:
1726 aa.add(x)
1734 aa.add(x)
1727 # make sure any files deleted in the local dirstate
1735 # make sure any files deleted in the local dirstate
1728 # are not in the add or change column of the patch
1736 # are not in the add or change column of the patch
1729 forget = []
1737 forget = []
1730 for x in d + r:
1738 for x in d + r:
1731 if x in aa:
1739 if x in aa:
1732 aa.remove(x)
1740 aa.remove(x)
1733 forget.append(x)
1741 forget.append(x)
1734 continue
1742 continue
1735 else:
1743 else:
1736 mm.discard(x)
1744 mm.discard(x)
1737 dd.add(x)
1745 dd.add(x)
1738
1746
1739 m = list(mm)
1747 m = list(mm)
1740 r = list(dd)
1748 r = list(dd)
1741 a = list(aa)
1749 a = list(aa)
1742
1750
1743 # create 'match' that includes the files to be recommitted.
1751 # create 'match' that includes the files to be recommitted.
1744 # apply match1 via repo.status to ensure correct case handling.
1752 # apply match1 via repo.status to ensure correct case handling.
1745 cm, ca, cr, cd = repo.status(patchparent, match=match1)[:4]
1753 cm, ca, cr, cd = repo.status(patchparent, match=match1)[:4]
1746 allmatches = set(cm + ca + cr + cd)
1754 allmatches = set(cm + ca + cr + cd)
1747 refreshchanges = [x.intersection(allmatches) for x in (mm, aa, dd)]
1755 refreshchanges = [x.intersection(allmatches) for x in (mm, aa, dd)]
1748
1756
1749 files = set(inclsubs)
1757 files = set(inclsubs)
1750 for x in refreshchanges:
1758 for x in refreshchanges:
1751 files.update(x)
1759 files.update(x)
1752 match = scmutil.matchfiles(repo, files)
1760 match = scmutil.matchfiles(repo, files)
1753
1761
1754 bmlist = repo[top].bookmarks()
1762 bmlist = repo[top].bookmarks()
1755
1763
1756 dsguard = None
1764 dsguard = None
1757 try:
1765 try:
1758 dsguard = dirstateguard.dirstateguard(repo, 'mq.refresh')
1766 dsguard = dirstateguard.dirstateguard(repo, 'mq.refresh')
1759 if diffopts.git or diffopts.upgrade:
1767 if diffopts.git or diffopts.upgrade:
1760 copies = {}
1768 copies = {}
1761 for dst in a:
1769 for dst in a:
1762 src = repo.dirstate.copied(dst)
1770 src = repo.dirstate.copied(dst)
1763 # during qfold, the source file for copies may
1771 # during qfold, the source file for copies may
1764 # be removed. Treat this as a simple add.
1772 # be removed. Treat this as a simple add.
1765 if src is not None and src in repo.dirstate:
1773 if src is not None and src in repo.dirstate:
1766 copies.setdefault(src, []).append(dst)
1774 copies.setdefault(src, []).append(dst)
1767 repo.dirstate.add(dst)
1775 repo.dirstate.add(dst)
1768 # remember the copies between patchparent and qtip
1776 # remember the copies between patchparent and qtip
1769 for dst in aaa:
1777 for dst in aaa:
1770 f = repo.file(dst)
1778 f = repo.file(dst)
1771 src = f.renamed(man[dst])
1779 src = f.renamed(man[dst])
1772 if src:
1780 if src:
1773 copies.setdefault(src[0], []).extend(
1781 copies.setdefault(src[0], []).extend(
1774 copies.get(dst, []))
1782 copies.get(dst, []))
1775 if dst in a:
1783 if dst in a:
1776 copies[src[0]].append(dst)
1784 copies[src[0]].append(dst)
1777 # we can't copy a file created by the patch itself
1785 # we can't copy a file created by the patch itself
1778 if dst in copies:
1786 if dst in copies:
1779 del copies[dst]
1787 del copies[dst]
1780 for src, dsts in copies.iteritems():
1788 for src, dsts in copies.iteritems():
1781 for dst in dsts:
1789 for dst in dsts:
1782 repo.dirstate.copy(src, dst)
1790 repo.dirstate.copy(src, dst)
1783 else:
1791 else:
1784 for dst in a:
1792 for dst in a:
1785 repo.dirstate.add(dst)
1793 repo.dirstate.add(dst)
1786 # Drop useless copy information
1794 # Drop useless copy information
1787 for f in list(repo.dirstate.copies()):
1795 for f in list(repo.dirstate.copies()):
1788 repo.dirstate.copy(None, f)
1796 repo.dirstate.copy(None, f)
1789 for f in r:
1797 for f in r:
1790 repo.dirstate.remove(f)
1798 repo.dirstate.remove(f)
1791 # if the patch excludes a modified file, mark that
1799 # if the patch excludes a modified file, mark that
1792 # file with mtime=0 so status can see it.
1800 # file with mtime=0 so status can see it.
1793 mm = []
1801 mm = []
1794 for i in xrange(len(m) - 1, -1, -1):
1802 for i in xrange(len(m) - 1, -1, -1):
1795 if not match1(m[i]):
1803 if not match1(m[i]):
1796 mm.append(m[i])
1804 mm.append(m[i])
1797 del m[i]
1805 del m[i]
1798 for f in m:
1806 for f in m:
1799 repo.dirstate.normal(f)
1807 repo.dirstate.normal(f)
1800 for f in mm:
1808 for f in mm:
1801 repo.dirstate.normallookup(f)
1809 repo.dirstate.normallookup(f)
1802 for f in forget:
1810 for f in forget:
1803 repo.dirstate.drop(f)
1811 repo.dirstate.drop(f)
1804
1812
1805 user = ph.user or changes[1]
1813 user = ph.user or changes[1]
1806
1814
1807 oldphase = repo[top].phase()
1815 oldphase = repo[top].phase()
1808
1816
1809 # assumes strip can roll itself back if interrupted
1817 # assumes strip can roll itself back if interrupted
1810 repo.setparents(*cparents)
1818 repo.setparents(*cparents)
1811 self.applied.pop()
1819 self.applied.pop()
1812 self.applieddirty = True
1820 self.applieddirty = True
1813 strip(self.ui, repo, [top], update=False, backup=False)
1821 strip(self.ui, repo, [top], update=False, backup=False)
1814 dsguard.close()
1822 dsguard.close()
1815 finally:
1823 finally:
1816 release(dsguard)
1824 release(dsguard)
1817
1825
1818 try:
1826 try:
1819 # might be nice to attempt to roll back strip after this
1827 # might be nice to attempt to roll back strip after this
1820
1828
1821 defaultmsg = "[mq]: %s" % patchfn
1829 defaultmsg = "[mq]: %s" % patchfn
1822 editor = cmdutil.getcommiteditor(editform=editform)
1830 editor = cmdutil.getcommiteditor(editform=editform)
1823 if edit:
1831 if edit:
1824 def finishdesc(desc):
1832 def finishdesc(desc):
1825 if desc.rstrip():
1833 if desc.rstrip():
1826 ph.setmessage(desc)
1834 ph.setmessage(desc)
1827 return desc
1835 return desc
1828 return defaultmsg
1836 return defaultmsg
1829 # i18n: this message is shown in editor with "HG: " prefix
1837 # i18n: this message is shown in editor with "HG: " prefix
1830 extramsg = _('Leave message empty to use default message.')
1838 extramsg = _('Leave message empty to use default message.')
1831 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1839 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1832 extramsg=extramsg,
1840 extramsg=extramsg,
1833 editform=editform)
1841 editform=editform)
1834 message = msg or "\n".join(ph.message)
1842 message = msg or "\n".join(ph.message)
1835 elif not msg:
1843 elif not msg:
1836 if not ph.message:
1844 if not ph.message:
1837 message = defaultmsg
1845 message = defaultmsg
1838 else:
1846 else:
1839 message = "\n".join(ph.message)
1847 message = "\n".join(ph.message)
1840 else:
1848 else:
1841 message = msg
1849 message = msg
1842 ph.setmessage(msg)
1850 ph.setmessage(msg)
1843
1851
1844 # Ensure we create a new changeset in the same phase than
1852 # Ensure we create a new changeset in the same phase than
1845 # the old one.
1853 # the old one.
1846 lock = tr = None
1854 lock = tr = None
1847 try:
1855 try:
1848 lock = repo.lock()
1856 lock = repo.lock()
1849 tr = repo.transaction('mq')
1857 tr = repo.transaction('mq')
1850 n = newcommit(repo, oldphase, message, user, ph.date,
1858 n = newcommit(repo, oldphase, message, user, ph.date,
1851 match=match, force=True, editor=editor)
1859 match=match, force=True, editor=editor)
1852 # only write patch after a successful commit
1860 # only write patch after a successful commit
1853 c = [list(x) for x in refreshchanges]
1861 c = [list(x) for x in refreshchanges]
1854 if inclsubs:
1862 if inclsubs:
1855 self.putsubstate2changes(substatestate, c)
1863 self.putsubstate2changes(substatestate, c)
1856 chunks = patchmod.diff(repo, patchparent,
1864 chunks = patchmod.diff(repo, patchparent,
1857 changes=c, opts=diffopts)
1865 changes=c, opts=diffopts)
1858 comments = bytes(ph)
1866 comments = bytes(ph)
1859 if comments:
1867 if comments:
1860 patchf.write(comments)
1868 patchf.write(comments)
1861 for chunk in chunks:
1869 for chunk in chunks:
1862 patchf.write(chunk)
1870 patchf.write(chunk)
1863 patchf.close()
1871 patchf.close()
1864
1872
1865 marks = repo._bookmarks
1873 marks = repo._bookmarks
1866 marks.applychanges(repo, tr, [(bm, n) for bm in bmlist])
1874 marks.applychanges(repo, tr, [(bm, n) for bm in bmlist])
1867 tr.close()
1875 tr.close()
1868
1876
1869 self.applied.append(statusentry(n, patchfn))
1877 self.applied.append(statusentry(n, patchfn))
1870 finally:
1878 finally:
1871 lockmod.release(tr, lock)
1879 lockmod.release(tr, lock)
1872 except: # re-raises
1880 except: # re-raises
1873 ctx = repo[cparents[0]]
1881 ctx = repo[cparents[0]]
1874 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1882 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1875 self.savedirty()
1883 self.savedirty()
1876 self.ui.warn(_('qrefresh interrupted while patch was popped! '
1884 self.ui.warn(_('qrefresh interrupted while patch was popped! '
1877 '(revert --all, qpush to recover)\n'))
1885 '(revert --all, qpush to recover)\n'))
1878 raise
1886 raise
1879 finally:
1887 finally:
1880 wlock.release()
1888 wlock.release()
1881 self.removeundo(repo)
1889 self.removeundo(repo)
1882
1890
1883 def init(self, repo, create=False):
1891 def init(self, repo, create=False):
1884 if not create and os.path.isdir(self.path):
1892 if not create and os.path.isdir(self.path):
1885 raise error.Abort(_("patch queue directory already exists"))
1893 raise error.Abort(_("patch queue directory already exists"))
1886 try:
1894 try:
1887 os.mkdir(self.path)
1895 os.mkdir(self.path)
1888 except OSError as inst:
1896 except OSError as inst:
1889 if inst.errno != errno.EEXIST or not create:
1897 if inst.errno != errno.EEXIST or not create:
1890 raise
1898 raise
1891 if create:
1899 if create:
1892 return self.qrepo(create=True)
1900 return self.qrepo(create=True)
1893
1901
1894 def unapplied(self, repo, patch=None):
1902 def unapplied(self, repo, patch=None):
1895 if patch and patch not in self.series:
1903 if patch and patch not in self.series:
1896 raise error.Abort(_("patch %s is not in series file") % patch)
1904 raise error.Abort(_("patch %s is not in series file") % patch)
1897 if not patch:
1905 if not patch:
1898 start = self.seriesend()
1906 start = self.seriesend()
1899 else:
1907 else:
1900 start = self.series.index(patch) + 1
1908 start = self.series.index(patch) + 1
1901 unapplied = []
1909 unapplied = []
1902 for i in xrange(start, len(self.series)):
1910 for i in xrange(start, len(self.series)):
1903 pushable, reason = self.pushable(i)
1911 pushable, reason = self.pushable(i)
1904 if pushable:
1912 if pushable:
1905 unapplied.append((i, self.series[i]))
1913 unapplied.append((i, self.series[i]))
1906 self.explainpushable(i)
1914 self.explainpushable(i)
1907 return unapplied
1915 return unapplied
1908
1916
1909 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1917 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1910 summary=False):
1918 summary=False):
1911 def displayname(pfx, patchname, state):
1919 def displayname(pfx, patchname, state):
1912 if pfx:
1920 if pfx:
1913 self.ui.write(pfx)
1921 self.ui.write(pfx)
1914 if summary:
1922 if summary:
1915 ph = patchheader(self.join(patchname), self.plainmode)
1923 ph = patchheader(self.join(patchname), self.plainmode)
1916 if ph.message:
1924 if ph.message:
1917 msg = ph.message[0]
1925 msg = ph.message[0]
1918 else:
1926 else:
1919 msg = ''
1927 msg = ''
1920
1928
1921 if self.ui.formatted():
1929 if self.ui.formatted():
1922 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1930 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1923 if width > 0:
1931 if width > 0:
1924 msg = stringutil.ellipsis(msg, width)
1932 msg = stringutil.ellipsis(msg, width)
1925 else:
1933 else:
1926 msg = ''
1934 msg = ''
1927 self.ui.write(patchname, label='qseries.' + state)
1935 self.ui.write(patchname, label='qseries.' + state)
1928 self.ui.write(': ')
1936 self.ui.write(': ')
1929 self.ui.write(msg, label='qseries.message.' + state)
1937 self.ui.write(msg, label='qseries.message.' + state)
1930 else:
1938 else:
1931 self.ui.write(patchname, label='qseries.' + state)
1939 self.ui.write(patchname, label='qseries.' + state)
1932 self.ui.write('\n')
1940 self.ui.write('\n')
1933
1941
1934 applied = set([p.name for p in self.applied])
1942 applied = set([p.name for p in self.applied])
1935 if length is None:
1943 if length is None:
1936 length = len(self.series) - start
1944 length = len(self.series) - start
1937 if not missing:
1945 if not missing:
1938 if self.ui.verbose:
1946 if self.ui.verbose:
1939 idxwidth = len("%d" % (start + length - 1))
1947 idxwidth = len("%d" % (start + length - 1))
1940 for i in xrange(start, start + length):
1948 for i in xrange(start, start + length):
1941 patch = self.series[i]
1949 patch = self.series[i]
1942 if patch in applied:
1950 if patch in applied:
1943 char, state = 'A', 'applied'
1951 char, state = 'A', 'applied'
1944 elif self.pushable(i)[0]:
1952 elif self.pushable(i)[0]:
1945 char, state = 'U', 'unapplied'
1953 char, state = 'U', 'unapplied'
1946 else:
1954 else:
1947 char, state = 'G', 'guarded'
1955 char, state = 'G', 'guarded'
1948 pfx = ''
1956 pfx = ''
1949 if self.ui.verbose:
1957 if self.ui.verbose:
1950 pfx = '%*d %s ' % (idxwidth, i, char)
1958 pfx = '%*d %s ' % (idxwidth, i, char)
1951 elif status and status != char:
1959 elif status and status != char:
1952 continue
1960 continue
1953 displayname(pfx, patch, state)
1961 displayname(pfx, patch, state)
1954 else:
1962 else:
1955 msng_list = []
1963 msng_list = []
1956 for root, dirs, files in os.walk(self.path):
1964 for root, dirs, files in os.walk(self.path):
1957 d = root[len(self.path) + 1:]
1965 d = root[len(self.path) + 1:]
1958 for f in files:
1966 for f in files:
1959 fl = os.path.join(d, f)
1967 fl = os.path.join(d, f)
1960 if (fl not in self.series and
1968 if (fl not in self.series and
1961 fl not in (self.statuspath, self.seriespath,
1969 fl not in (self.statuspath, self.seriespath,
1962 self.guardspath)
1970 self.guardspath)
1963 and not fl.startswith('.')):
1971 and not fl.startswith('.')):
1964 msng_list.append(fl)
1972 msng_list.append(fl)
1965 for x in sorted(msng_list):
1973 for x in sorted(msng_list):
1966 pfx = self.ui.verbose and ('D ') or ''
1974 pfx = self.ui.verbose and ('D ') or ''
1967 displayname(pfx, x, 'missing')
1975 displayname(pfx, x, 'missing')
1968
1976
1969 def issaveline(self, l):
1977 def issaveline(self, l):
1970 if l.name == '.hg.patches.save.line':
1978 if l.name == '.hg.patches.save.line':
1971 return True
1979 return True
1972
1980
1973 def qrepo(self, create=False):
1981 def qrepo(self, create=False):
1974 ui = self.baseui.copy()
1982 ui = self.baseui.copy()
1975 # copy back attributes set by ui.pager()
1983 # copy back attributes set by ui.pager()
1976 if self.ui.pageractive and not ui.pageractive:
1984 if self.ui.pageractive and not ui.pageractive:
1977 ui.pageractive = self.ui.pageractive
1985 ui.pageractive = self.ui.pageractive
1978 # internal config: ui.formatted
1986 # internal config: ui.formatted
1979 ui.setconfig('ui', 'formatted',
1987 ui.setconfig('ui', 'formatted',
1980 self.ui.config('ui', 'formatted'), 'mqpager')
1988 self.ui.config('ui', 'formatted'), 'mqpager')
1981 ui.setconfig('ui', 'interactive',
1989 ui.setconfig('ui', 'interactive',
1982 self.ui.config('ui', 'interactive'), 'mqpager')
1990 self.ui.config('ui', 'interactive'), 'mqpager')
1983 if create or os.path.isdir(self.join(".hg")):
1991 if create or os.path.isdir(self.join(".hg")):
1984 return hg.repository(ui, path=self.path, create=create)
1992 return hg.repository(ui, path=self.path, create=create)
1985
1993
1986 def restore(self, repo, rev, delete=None, qupdate=None):
1994 def restore(self, repo, rev, delete=None, qupdate=None):
1987 desc = repo[rev].description().strip()
1995 desc = repo[rev].description().strip()
1988 lines = desc.splitlines()
1996 lines = desc.splitlines()
1989 i = 0
1997 i = 0
1990 datastart = None
1998 datastart = None
1991 series = []
1999 series = []
1992 applied = []
2000 applied = []
1993 qpp = None
2001 qpp = None
1994 for i, line in enumerate(lines):
2002 for i, line in enumerate(lines):
1995 if line == 'Patch Data:':
2003 if line == 'Patch Data:':
1996 datastart = i + 1
2004 datastart = i + 1
1997 elif line.startswith('Dirstate:'):
2005 elif line.startswith('Dirstate:'):
1998 l = line.rstrip()
2006 l = line.rstrip()
1999 l = l[10:].split(' ')
2007 l = l[10:].split(' ')
2000 qpp = [bin(x) for x in l]
2008 qpp = [bin(x) for x in l]
2001 elif datastart is not None:
2009 elif datastart is not None:
2002 l = line.rstrip()
2010 l = line.rstrip()
2003 n, name = l.split(':', 1)
2011 n, name = l.split(':', 1)
2004 if n:
2012 if n:
2005 applied.append(statusentry(bin(n), name))
2013 applied.append(statusentry(bin(n), name))
2006 else:
2014 else:
2007 series.append(l)
2015 series.append(l)
2008 if datastart is None:
2016 if datastart is None:
2009 self.ui.warn(_("no saved patch data found\n"))
2017 self.ui.warn(_("no saved patch data found\n"))
2010 return 1
2018 return 1
2011 self.ui.warn(_("restoring status: %s\n") % lines[0])
2019 self.ui.warn(_("restoring status: %s\n") % lines[0])
2012 self.fullseries = series
2020 self.fullseries = series
2013 self.applied = applied
2021 self.applied = applied
2014 self.parseseries()
2022 self.parseseries()
2015 self.seriesdirty = True
2023 self.seriesdirty = True
2016 self.applieddirty = True
2024 self.applieddirty = True
2017 heads = repo.changelog.heads()
2025 heads = repo.changelog.heads()
2018 if delete:
2026 if delete:
2019 if rev not in heads:
2027 if rev not in heads:
2020 self.ui.warn(_("save entry has children, leaving it alone\n"))
2028 self.ui.warn(_("save entry has children, leaving it alone\n"))
2021 else:
2029 else:
2022 self.ui.warn(_("removing save entry %s\n") % short(rev))
2030 self.ui.warn(_("removing save entry %s\n") % short(rev))
2023 pp = repo.dirstate.parents()
2031 pp = repo.dirstate.parents()
2024 if rev in pp:
2032 if rev in pp:
2025 update = True
2033 update = True
2026 else:
2034 else:
2027 update = False
2035 update = False
2028 strip(self.ui, repo, [rev], update=update, backup=False)
2036 strip(self.ui, repo, [rev], update=update, backup=False)
2029 if qpp:
2037 if qpp:
2030 self.ui.warn(_("saved queue repository parents: %s %s\n") %
2038 self.ui.warn(_("saved queue repository parents: %s %s\n") %
2031 (short(qpp[0]), short(qpp[1])))
2039 (short(qpp[0]), short(qpp[1])))
2032 if qupdate:
2040 if qupdate:
2033 self.ui.status(_("updating queue directory\n"))
2041 self.ui.status(_("updating queue directory\n"))
2034 r = self.qrepo()
2042 r = self.qrepo()
2035 if not r:
2043 if not r:
2036 self.ui.warn(_("unable to load queue repository\n"))
2044 self.ui.warn(_("unable to load queue repository\n"))
2037 return 1
2045 return 1
2038 hg.clean(r, qpp[0])
2046 hg.clean(r, qpp[0])
2039
2047
2040 def save(self, repo, msg=None):
2048 def save(self, repo, msg=None):
2041 if not self.applied:
2049 if not self.applied:
2042 self.ui.warn(_("save: no patches applied, exiting\n"))
2050 self.ui.warn(_("save: no patches applied, exiting\n"))
2043 return 1
2051 return 1
2044 if self.issaveline(self.applied[-1]):
2052 if self.issaveline(self.applied[-1]):
2045 self.ui.warn(_("status is already saved\n"))
2053 self.ui.warn(_("status is already saved\n"))
2046 return 1
2054 return 1
2047
2055
2048 if not msg:
2056 if not msg:
2049 msg = _("hg patches saved state")
2057 msg = _("hg patches saved state")
2050 else:
2058 else:
2051 msg = "hg patches: " + msg.rstrip('\r\n')
2059 msg = "hg patches: " + msg.rstrip('\r\n')
2052 r = self.qrepo()
2060 r = self.qrepo()
2053 if r:
2061 if r:
2054 pp = r.dirstate.parents()
2062 pp = r.dirstate.parents()
2055 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
2063 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
2056 msg += "\n\nPatch Data:\n"
2064 msg += "\n\nPatch Data:\n"
2057 msg += ''.join('%s\n' % x for x in self.applied)
2065 msg += ''.join('%s\n' % x for x in self.applied)
2058 msg += ''.join(':%s\n' % x for x in self.fullseries)
2066 msg += ''.join(':%s\n' % x for x in self.fullseries)
2059 n = repo.commit(msg, force=True)
2067 n = repo.commit(msg, force=True)
2060 if not n:
2068 if not n:
2061 self.ui.warn(_("repo commit failed\n"))
2069 self.ui.warn(_("repo commit failed\n"))
2062 return 1
2070 return 1
2063 self.applied.append(statusentry(n, '.hg.patches.save.line'))
2071 self.applied.append(statusentry(n, '.hg.patches.save.line'))
2064 self.applieddirty = True
2072 self.applieddirty = True
2065 self.removeundo(repo)
2073 self.removeundo(repo)
2066
2074
2067 def fullseriesend(self):
2075 def fullseriesend(self):
2068 if self.applied:
2076 if self.applied:
2069 p = self.applied[-1].name
2077 p = self.applied[-1].name
2070 end = self.findseries(p)
2078 end = self.findseries(p)
2071 if end is None:
2079 if end is None:
2072 return len(self.fullseries)
2080 return len(self.fullseries)
2073 return end + 1
2081 return end + 1
2074 return 0
2082 return 0
2075
2083
2076 def seriesend(self, all_patches=False):
2084 def seriesend(self, all_patches=False):
2077 """If all_patches is False, return the index of the next pushable patch
2085 """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
2086 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.
2087 index of the first patch past the last applied one.
2080 """
2088 """
2081 end = 0
2089 end = 0
2082 def nextpatch(start):
2090 def nextpatch(start):
2083 if all_patches or start >= len(self.series):
2091 if all_patches or start >= len(self.series):
2084 return start
2092 return start
2085 for i in xrange(start, len(self.series)):
2093 for i in xrange(start, len(self.series)):
2086 p, reason = self.pushable(i)
2094 p, reason = self.pushable(i)
2087 if p:
2095 if p:
2088 return i
2096 return i
2089 self.explainpushable(i)
2097 self.explainpushable(i)
2090 return len(self.series)
2098 return len(self.series)
2091 if self.applied:
2099 if self.applied:
2092 p = self.applied[-1].name
2100 p = self.applied[-1].name
2093 try:
2101 try:
2094 end = self.series.index(p)
2102 end = self.series.index(p)
2095 except ValueError:
2103 except ValueError:
2096 return 0
2104 return 0
2097 return nextpatch(end + 1)
2105 return nextpatch(end + 1)
2098 return nextpatch(end)
2106 return nextpatch(end)
2099
2107
2100 def appliedname(self, index):
2108 def appliedname(self, index):
2101 pname = self.applied[index].name
2109 pname = self.applied[index].name
2102 if not self.ui.verbose:
2110 if not self.ui.verbose:
2103 p = pname
2111 p = pname
2104 else:
2112 else:
2105 p = ("%d" % self.series.index(pname)) + " " + pname
2113 p = ("%d" % self.series.index(pname)) + " " + pname
2106 return p
2114 return p
2107
2115
2108 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
2116 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
2109 force=None, git=False):
2117 force=None, git=False):
2110 def checkseries(patchname):
2118 def checkseries(patchname):
2111 if patchname in self.series:
2119 if patchname in self.series:
2112 raise error.Abort(_('patch %s is already in the series file')
2120 raise error.Abort(_('patch %s is already in the series file')
2113 % patchname)
2121 % patchname)
2114
2122
2115 if rev:
2123 if rev:
2116 if files:
2124 if files:
2117 raise error.Abort(_('option "-r" not valid when importing '
2125 raise error.Abort(_('option "-r" not valid when importing '
2118 'files'))
2126 'files'))
2119 rev = scmutil.revrange(repo, rev)
2127 rev = scmutil.revrange(repo, rev)
2120 rev.sort(reverse=True)
2128 rev.sort(reverse=True)
2121 elif not files:
2129 elif not files:
2122 raise error.Abort(_('no files or revisions specified'))
2130 raise error.Abort(_('no files or revisions specified'))
2123 if (len(files) > 1 or len(rev) > 1) and patchname:
2131 if (len(files) > 1 or len(rev) > 1) and patchname:
2124 raise error.Abort(_('option "-n" not valid when importing multiple '
2132 raise error.Abort(_('option "-n" not valid when importing multiple '
2125 'patches'))
2133 'patches'))
2126 imported = []
2134 imported = []
2127 if rev:
2135 if rev:
2128 # If mq patches are applied, we can only import revisions
2136 # If mq patches are applied, we can only import revisions
2129 # that form a linear path to qbase.
2137 # that form a linear path to qbase.
2130 # Otherwise, they should form a linear path to a head.
2138 # Otherwise, they should form a linear path to a head.
2131 heads = repo.changelog.heads(repo.changelog.node(rev.first()))
2139 heads = repo.changelog.heads(repo.changelog.node(rev.first()))
2132 if len(heads) > 1:
2140 if len(heads) > 1:
2133 raise error.Abort(_('revision %d is the root of more than one '
2141 raise error.Abort(_('revision %d is the root of more than one '
2134 'branch') % rev.last())
2142 'branch') % rev.last())
2135 if self.applied:
2143 if self.applied:
2136 base = repo.changelog.node(rev.first())
2144 base = repo.changelog.node(rev.first())
2137 if base in [n.node for n in self.applied]:
2145 if base in [n.node for n in self.applied]:
2138 raise error.Abort(_('revision %d is already managed')
2146 raise error.Abort(_('revision %d is already managed')
2139 % rev.first())
2147 % rev.first())
2140 if heads != [self.applied[-1].node]:
2148 if heads != [self.applied[-1].node]:
2141 raise error.Abort(_('revision %d is not the parent of '
2149 raise error.Abort(_('revision %d is not the parent of '
2142 'the queue') % rev.first())
2150 'the queue') % rev.first())
2143 base = repo.changelog.rev(self.applied[0].node)
2151 base = repo.changelog.rev(self.applied[0].node)
2144 lastparent = repo.changelog.parentrevs(base)[0]
2152 lastparent = repo.changelog.parentrevs(base)[0]
2145 else:
2153 else:
2146 if heads != [repo.changelog.node(rev.first())]:
2154 if heads != [repo.changelog.node(rev.first())]:
2147 raise error.Abort(_('revision %d has unmanaged children')
2155 raise error.Abort(_('revision %d has unmanaged children')
2148 % rev.first())
2156 % rev.first())
2149 lastparent = None
2157 lastparent = None
2150
2158
2151 diffopts = self.diffopts({'git': git})
2159 diffopts = self.diffopts({'git': git})
2152 with repo.transaction('qimport') as tr:
2160 with repo.transaction('qimport') as tr:
2153 for r in rev:
2161 for r in rev:
2154 if not repo[r].mutable():
2162 if not repo[r].mutable():
2155 raise error.Abort(_('revision %d is not mutable') % r,
2163 raise error.Abort(_('revision %d is not mutable') % r,
2156 hint=_("see 'hg help phases' "
2164 hint=_("see 'hg help phases' "
2157 'for details'))
2165 'for details'))
2158 p1, p2 = repo.changelog.parentrevs(r)
2166 p1, p2 = repo.changelog.parentrevs(r)
2159 n = repo.changelog.node(r)
2167 n = repo.changelog.node(r)
2160 if p2 != nullrev:
2168 if p2 != nullrev:
2161 raise error.Abort(_('cannot import merge revision %d')
2169 raise error.Abort(_('cannot import merge revision %d')
2162 % r)
2170 % r)
2163 if lastparent and lastparent != r:
2171 if lastparent and lastparent != r:
2164 raise error.Abort(_('revision %d is not the parent of '
2172 raise error.Abort(_('revision %d is not the parent of '
2165 '%d')
2173 '%d')
2166 % (r, lastparent))
2174 % (r, lastparent))
2167 lastparent = p1
2175 lastparent = p1
2168
2176
2169 if not patchname:
2177 if not patchname:
2170 patchname = self.makepatchname(
2178 patchname = self.makepatchname(
2171 repo[r].description().split('\n', 1)[0],
2179 repo[r].description().split('\n', 1)[0],
2172 '%d.diff' % r)
2180 '%d.diff' % r)
2173 checkseries(patchname)
2181 checkseries(patchname)
2174 self.checkpatchname(patchname, force)
2182 self.checkpatchname(patchname, force)
2175 self.fullseries.insert(0, patchname)
2183 self.fullseries.insert(0, patchname)
2176
2184
2177 patchf = self.opener(patchname, "w")
2185 patchf = self.opener(patchname, "w")
2178 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
2186 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
2179 patchf.close()
2187 patchf.close()
2180
2188
2181 se = statusentry(n, patchname)
2189 se = statusentry(n, patchname)
2182 self.applied.insert(0, se)
2190 self.applied.insert(0, se)
2183
2191
2184 self.added.append(patchname)
2192 self.added.append(patchname)
2185 imported.append(patchname)
2193 imported.append(patchname)
2186 patchname = None
2194 patchname = None
2187 if rev and repo.ui.configbool('mq', 'secret'):
2195 if rev and repo.ui.configbool('mq', 'secret'):
2188 # if we added anything with --rev, move the secret root
2196 # if we added anything with --rev, move the secret root
2189 phases.retractboundary(repo, tr, phases.secret, [n])
2197 phases.retractboundary(repo, tr, phases.secret, [n])
2190 self.parseseries()
2198 self.parseseries()
2191 self.applieddirty = True
2199 self.applieddirty = True
2192 self.seriesdirty = True
2200 self.seriesdirty = True
2193
2201
2194 for i, filename in enumerate(files):
2202 for i, filename in enumerate(files):
2195 if existing:
2203 if existing:
2196 if filename == '-':
2204 if filename == '-':
2197 raise error.Abort(_('-e is incompatible with import from -')
2205 raise error.Abort(_('-e is incompatible with import from -')
2198 )
2206 )
2199 filename = normname(filename)
2207 filename = normname(filename)
2200 self.checkreservedname(filename)
2208 self.checkreservedname(filename)
2201 if util.url(filename).islocal():
2209 if util.url(filename).islocal():
2202 originpath = self.join(filename)
2210 originpath = self.join(filename)
2203 if not os.path.isfile(originpath):
2211 if not os.path.isfile(originpath):
2204 raise error.Abort(
2212 raise error.Abort(
2205 _("patch %s does not exist") % filename)
2213 _("patch %s does not exist") % filename)
2206
2214
2207 if patchname:
2215 if patchname:
2208 self.checkpatchname(patchname, force)
2216 self.checkpatchname(patchname, force)
2209
2217
2210 self.ui.write(_('renaming %s to %s\n')
2218 self.ui.write(_('renaming %s to %s\n')
2211 % (filename, patchname))
2219 % (filename, patchname))
2212 util.rename(originpath, self.join(patchname))
2220 util.rename(originpath, self.join(patchname))
2213 else:
2221 else:
2214 patchname = filename
2222 patchname = filename
2215
2223
2216 else:
2224 else:
2217 if filename == '-' and not patchname:
2225 if filename == '-' and not patchname:
2218 raise error.Abort(_('need --name to import a patch from -'))
2226 raise error.Abort(_('need --name to import a patch from -'))
2219 elif not patchname:
2227 elif not patchname:
2220 patchname = normname(os.path.basename(filename.rstrip('/')))
2228 patchname = normname(os.path.basename(filename.rstrip('/')))
2221 self.checkpatchname(patchname, force)
2229 self.checkpatchname(patchname, force)
2222 try:
2230 try:
2223 if filename == '-':
2231 if filename == '-':
2224 text = self.ui.fin.read()
2232 text = self.ui.fin.read()
2225 else:
2233 else:
2226 fp = hg.openpath(self.ui, filename)
2234 fp = hg.openpath(self.ui, filename)
2227 text = fp.read()
2235 text = fp.read()
2228 fp.close()
2236 fp.close()
2229 except (OSError, IOError):
2237 except (OSError, IOError):
2230 raise error.Abort(_("unable to read file %s") % filename)
2238 raise error.Abort(_("unable to read file %s") % filename)
2231 patchf = self.opener(patchname, "w")
2239 patchf = self.opener(patchname, "w")
2232 patchf.write(text)
2240 patchf.write(text)
2233 patchf.close()
2241 patchf.close()
2234 if not force:
2242 if not force:
2235 checkseries(patchname)
2243 checkseries(patchname)
2236 if patchname not in self.series:
2244 if patchname not in self.series:
2237 index = self.fullseriesend() + i
2245 index = self.fullseriesend() + i
2238 self.fullseries[index:index] = [patchname]
2246 self.fullseries[index:index] = [patchname]
2239 self.parseseries()
2247 self.parseseries()
2240 self.seriesdirty = True
2248 self.seriesdirty = True
2241 self.ui.warn(_("adding %s to series file\n") % patchname)
2249 self.ui.warn(_("adding %s to series file\n") % patchname)
2242 self.added.append(patchname)
2250 self.added.append(patchname)
2243 imported.append(patchname)
2251 imported.append(patchname)
2244 patchname = None
2252 patchname = None
2245
2253
2246 self.removeundo(repo)
2254 self.removeundo(repo)
2247 return imported
2255 return imported
2248
2256
2249 def fixkeepchangesopts(ui, opts):
2257 def fixkeepchangesopts(ui, opts):
2250 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2258 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2251 or opts.get('exact')):
2259 or opts.get('exact')):
2252 return opts
2260 return opts
2253 opts = dict(opts)
2261 opts = dict(opts)
2254 opts['keep_changes'] = True
2262 opts['keep_changes'] = True
2255 return opts
2263 return opts
2256
2264
2257 @command("qdelete|qremove|qrm",
2265 @command("qdelete|qremove|qrm",
2258 [('k', 'keep', None, _('keep patch file')),
2266 [('k', 'keep', None, _('keep patch file')),
2259 ('r', 'rev', [],
2267 ('r', 'rev', [],
2260 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2268 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2261 _('hg qdelete [-k] [PATCH]...'))
2269 _('hg qdelete [-k] [PATCH]...'))
2262 def delete(ui, repo, *patches, **opts):
2270 def delete(ui, repo, *patches, **opts):
2263 """remove patches from queue
2271 """remove patches from queue
2264
2272
2265 The patches must not be applied, and at least one patch is required. Exact
2273 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
2274 patch identifiers must be given. With -k/--keep, the patch files are
2267 preserved in the patch directory.
2275 preserved in the patch directory.
2268
2276
2269 To stop managing a patch and move it into permanent history,
2277 To stop managing a patch and move it into permanent history,
2270 use the :hg:`qfinish` command."""
2278 use the :hg:`qfinish` command."""
2271 q = repo.mq
2279 q = repo.mq
2272 q.delete(repo, patches, pycompat.byteskwargs(opts))
2280 q.delete(repo, patches, pycompat.byteskwargs(opts))
2273 q.savedirty()
2281 q.savedirty()
2274 return 0
2282 return 0
2275
2283
2276 @command("qapplied",
2284 @command("qapplied",
2277 [('1', 'last', None, _('show only the preceding applied patch'))
2285 [('1', 'last', None, _('show only the preceding applied patch'))
2278 ] + seriesopts,
2286 ] + seriesopts,
2279 _('hg qapplied [-1] [-s] [PATCH]'))
2287 _('hg qapplied [-1] [-s] [PATCH]'))
2280 def applied(ui, repo, patch=None, **opts):
2288 def applied(ui, repo, patch=None, **opts):
2281 """print the patches already applied
2289 """print the patches already applied
2282
2290
2283 Returns 0 on success."""
2291 Returns 0 on success."""
2284
2292
2285 q = repo.mq
2293 q = repo.mq
2286 opts = pycompat.byteskwargs(opts)
2294 opts = pycompat.byteskwargs(opts)
2287
2295
2288 if patch:
2296 if patch:
2289 if patch not in q.series:
2297 if patch not in q.series:
2290 raise error.Abort(_("patch %s is not in series file") % patch)
2298 raise error.Abort(_("patch %s is not in series file") % patch)
2291 end = q.series.index(patch) + 1
2299 end = q.series.index(patch) + 1
2292 else:
2300 else:
2293 end = q.seriesend(True)
2301 end = q.seriesend(True)
2294
2302
2295 if opts.get('last') and not end:
2303 if opts.get('last') and not end:
2296 ui.write(_("no patches applied\n"))
2304 ui.write(_("no patches applied\n"))
2297 return 1
2305 return 1
2298 elif opts.get('last') and end == 1:
2306 elif opts.get('last') and end == 1:
2299 ui.write(_("only one patch applied\n"))
2307 ui.write(_("only one patch applied\n"))
2300 return 1
2308 return 1
2301 elif opts.get('last'):
2309 elif opts.get('last'):
2302 start = end - 2
2310 start = end - 2
2303 end = 1
2311 end = 1
2304 else:
2312 else:
2305 start = 0
2313 start = 0
2306
2314
2307 q.qseries(repo, length=end, start=start, status='A',
2315 q.qseries(repo, length=end, start=start, status='A',
2308 summary=opts.get('summary'))
2316 summary=opts.get('summary'))
2309
2317
2310
2318
2311 @command("qunapplied",
2319 @command("qunapplied",
2312 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2320 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2313 _('hg qunapplied [-1] [-s] [PATCH]'))
2321 _('hg qunapplied [-1] [-s] [PATCH]'))
2314 def unapplied(ui, repo, patch=None, **opts):
2322 def unapplied(ui, repo, patch=None, **opts):
2315 """print the patches not yet applied
2323 """print the patches not yet applied
2316
2324
2317 Returns 0 on success."""
2325 Returns 0 on success."""
2318
2326
2319 q = repo.mq
2327 q = repo.mq
2320 opts = pycompat.byteskwargs(opts)
2328 opts = pycompat.byteskwargs(opts)
2321 if patch:
2329 if patch:
2322 if patch not in q.series:
2330 if patch not in q.series:
2323 raise error.Abort(_("patch %s is not in series file") % patch)
2331 raise error.Abort(_("patch %s is not in series file") % patch)
2324 start = q.series.index(patch) + 1
2332 start = q.series.index(patch) + 1
2325 else:
2333 else:
2326 start = q.seriesend(True)
2334 start = q.seriesend(True)
2327
2335
2328 if start == len(q.series) and opts.get('first'):
2336 if start == len(q.series) and opts.get('first'):
2329 ui.write(_("all patches applied\n"))
2337 ui.write(_("all patches applied\n"))
2330 return 1
2338 return 1
2331
2339
2332 if opts.get('first'):
2340 if opts.get('first'):
2333 length = 1
2341 length = 1
2334 else:
2342 else:
2335 length = None
2343 length = None
2336 q.qseries(repo, start=start, length=length, status='U',
2344 q.qseries(repo, start=start, length=length, status='U',
2337 summary=opts.get('summary'))
2345 summary=opts.get('summary'))
2338
2346
2339 @command("qimport",
2347 @command("qimport",
2340 [('e', 'existing', None, _('import file in patch directory')),
2348 [('e', 'existing', None, _('import file in patch directory')),
2341 ('n', 'name', '',
2349 ('n', 'name', '',
2342 _('name of patch file'), _('NAME')),
2350 _('name of patch file'), _('NAME')),
2343 ('f', 'force', None, _('overwrite existing files')),
2351 ('f', 'force', None, _('overwrite existing files')),
2344 ('r', 'rev', [],
2352 ('r', 'rev', [],
2345 _('place existing revisions under mq control'), _('REV')),
2353 _('place existing revisions under mq control'), _('REV')),
2346 ('g', 'git', None, _('use git extended diff format')),
2354 ('g', 'git', None, _('use git extended diff format')),
2347 ('P', 'push', None, _('qpush after importing'))],
2355 ('P', 'push', None, _('qpush after importing'))],
2348 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2356 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2349 def qimport(ui, repo, *filename, **opts):
2357 def qimport(ui, repo, *filename, **opts):
2350 """import a patch or existing changeset
2358 """import a patch or existing changeset
2351
2359
2352 The patch is inserted into the series after the last applied
2360 The patch is inserted into the series after the last applied
2353 patch. If no patches have been applied, qimport prepends the patch
2361 patch. If no patches have been applied, qimport prepends the patch
2354 to the series.
2362 to the series.
2355
2363
2356 The patch will have the same name as its source file unless you
2364 The patch will have the same name as its source file unless you
2357 give it a new one with -n/--name.
2365 give it a new one with -n/--name.
2358
2366
2359 You can register an existing patch inside the patch directory with
2367 You can register an existing patch inside the patch directory with
2360 the -e/--existing flag.
2368 the -e/--existing flag.
2361
2369
2362 With -f/--force, an existing patch of the same name will be
2370 With -f/--force, an existing patch of the same name will be
2363 overwritten.
2371 overwritten.
2364
2372
2365 An existing changeset may be placed under mq control with -r/--rev
2373 An existing changeset may be placed under mq control with -r/--rev
2366 (e.g. qimport --rev . -n patch will place the current revision
2374 (e.g. qimport --rev . -n patch will place the current revision
2367 under mq control). With -g/--git, patches imported with --rev will
2375 under mq control). With -g/--git, patches imported with --rev will
2368 use the git diff format. See the diffs help topic for information
2376 use the git diff format. See the diffs help topic for information
2369 on why this is important for preserving rename/copy information
2377 on why this is important for preserving rename/copy information
2370 and permission changes. Use :hg:`qfinish` to remove changesets
2378 and permission changes. Use :hg:`qfinish` to remove changesets
2371 from mq control.
2379 from mq control.
2372
2380
2373 To import a patch from standard input, pass - as the patch file.
2381 To import a patch from standard input, pass - as the patch file.
2374 When importing from standard input, a patch name must be specified
2382 When importing from standard input, a patch name must be specified
2375 using the --name flag.
2383 using the --name flag.
2376
2384
2377 To import an existing patch while renaming it::
2385 To import an existing patch while renaming it::
2378
2386
2379 hg qimport -e existing-patch -n new-name
2387 hg qimport -e existing-patch -n new-name
2380
2388
2381 Returns 0 if import succeeded.
2389 Returns 0 if import succeeded.
2382 """
2390 """
2383 opts = pycompat.byteskwargs(opts)
2391 opts = pycompat.byteskwargs(opts)
2384 with repo.lock(): # cause this may move phase
2392 with repo.lock(): # cause this may move phase
2385 q = repo.mq
2393 q = repo.mq
2386 try:
2394 try:
2387 imported = q.qimport(
2395 imported = q.qimport(
2388 repo, filename, patchname=opts.get('name'),
2396 repo, filename, patchname=opts.get('name'),
2389 existing=opts.get('existing'), force=opts.get('force'),
2397 existing=opts.get('existing'), force=opts.get('force'),
2390 rev=opts.get('rev'), git=opts.get('git'))
2398 rev=opts.get('rev'), git=opts.get('git'))
2391 finally:
2399 finally:
2392 q.savedirty()
2400 q.savedirty()
2393
2401
2394 if imported and opts.get('push') and not opts.get('rev'):
2402 if imported and opts.get('push') and not opts.get('rev'):
2395 return q.push(repo, imported[-1])
2403 return q.push(repo, imported[-1])
2396 return 0
2404 return 0
2397
2405
2398 def qinit(ui, repo, create):
2406 def qinit(ui, repo, create):
2399 """initialize a new queue repository
2407 """initialize a new queue repository
2400
2408
2401 This command also creates a series file for ordering patches, and
2409 This command also creates a series file for ordering patches, and
2402 an mq-specific .hgignore file in the queue repository, to exclude
2410 an mq-specific .hgignore file in the queue repository, to exclude
2403 the status and guards files (these contain mostly transient state).
2411 the status and guards files (these contain mostly transient state).
2404
2412
2405 Returns 0 if initialization succeeded."""
2413 Returns 0 if initialization succeeded."""
2406 q = repo.mq
2414 q = repo.mq
2407 r = q.init(repo, create)
2415 r = q.init(repo, create)
2408 q.savedirty()
2416 q.savedirty()
2409 if r:
2417 if r:
2410 if not os.path.exists(r.wjoin('.hgignore')):
2418 if not os.path.exists(r.wjoin('.hgignore')):
2411 fp = r.wvfs('.hgignore', 'w')
2419 fp = r.wvfs('.hgignore', 'w')
2412 fp.write('^\\.hg\n')
2420 fp.write('^\\.hg\n')
2413 fp.write('^\\.mq\n')
2421 fp.write('^\\.mq\n')
2414 fp.write('syntax: glob\n')
2422 fp.write('syntax: glob\n')
2415 fp.write('status\n')
2423 fp.write('status\n')
2416 fp.write('guards\n')
2424 fp.write('guards\n')
2417 fp.close()
2425 fp.close()
2418 if not os.path.exists(r.wjoin('series')):
2426 if not os.path.exists(r.wjoin('series')):
2419 r.wvfs('series', 'w').close()
2427 r.wvfs('series', 'w').close()
2420 r[None].add(['.hgignore', 'series'])
2428 r[None].add(['.hgignore', 'series'])
2421 commands.add(ui, r)
2429 commands.add(ui, r)
2422 return 0
2430 return 0
2423
2431
2424 @command("^qinit",
2432 @command("^qinit",
2425 [('c', 'create-repo', None, _('create queue repository'))],
2433 [('c', 'create-repo', None, _('create queue repository'))],
2426 _('hg qinit [-c]'))
2434 _('hg qinit [-c]'))
2427 def init(ui, repo, **opts):
2435 def init(ui, repo, **opts):
2428 """init a new queue repository (DEPRECATED)
2436 """init a new queue repository (DEPRECATED)
2429
2437
2430 The queue repository is unversioned by default. If
2438 The queue repository is unversioned by default. If
2431 -c/--create-repo is specified, qinit will create a separate nested
2439 -c/--create-repo is specified, qinit will create a separate nested
2432 repository for patches (qinit -c may also be run later to convert
2440 repository for patches (qinit -c may also be run later to convert
2433 an unversioned patch repository into a versioned one). You can use
2441 an unversioned patch repository into a versioned one). You can use
2434 qcommit to commit changes to this queue repository.
2442 qcommit to commit changes to this queue repository.
2435
2443
2436 This command is deprecated. Without -c, it's implied by other relevant
2444 This command is deprecated. Without -c, it's implied by other relevant
2437 commands. With -c, use :hg:`init --mq` instead."""
2445 commands. With -c, use :hg:`init --mq` instead."""
2438 return qinit(ui, repo, create=opts.get(r'create_repo'))
2446 return qinit(ui, repo, create=opts.get(r'create_repo'))
2439
2447
2440 @command("qclone",
2448 @command("qclone",
2441 [('', 'pull', None, _('use pull protocol to copy metadata')),
2449 [('', 'pull', None, _('use pull protocol to copy metadata')),
2442 ('U', 'noupdate', None,
2450 ('U', 'noupdate', None,
2443 _('do not update the new working directories')),
2451 _('do not update the new working directories')),
2444 ('', 'uncompressed', None,
2452 ('', 'uncompressed', None,
2445 _('use uncompressed transfer (fast over LAN)')),
2453 _('use uncompressed transfer (fast over LAN)')),
2446 ('p', 'patches', '',
2454 ('p', 'patches', '',
2447 _('location of source patch repository'), _('REPO')),
2455 _('location of source patch repository'), _('REPO')),
2448 ] + cmdutil.remoteopts,
2456 ] + cmdutil.remoteopts,
2449 _('hg qclone [OPTION]... SOURCE [DEST]'),
2457 _('hg qclone [OPTION]... SOURCE [DEST]'),
2450 norepo=True)
2458 norepo=True)
2451 def clone(ui, source, dest=None, **opts):
2459 def clone(ui, source, dest=None, **opts):
2452 '''clone main and patch repository at same time
2460 '''clone main and patch repository at same time
2453
2461
2454 If source is local, destination will have no patches applied. If
2462 If source is local, destination will have no patches applied. If
2455 source is remote, this command can not check if patches are
2463 source is remote, this command can not check if patches are
2456 applied in source, so cannot guarantee that patches are not
2464 applied in source, so cannot guarantee that patches are not
2457 applied in destination. If you clone remote repository, be sure
2465 applied in destination. If you clone remote repository, be sure
2458 before that it has no patches applied.
2466 before that it has no patches applied.
2459
2467
2460 Source patch repository is looked for in <src>/.hg/patches by
2468 Source patch repository is looked for in <src>/.hg/patches by
2461 default. Use -p <url> to change.
2469 default. Use -p <url> to change.
2462
2470
2463 The patch directory must be a nested Mercurial repository, as
2471 The patch directory must be a nested Mercurial repository, as
2464 would be created by :hg:`init --mq`.
2472 would be created by :hg:`init --mq`.
2465
2473
2466 Return 0 on success.
2474 Return 0 on success.
2467 '''
2475 '''
2468 opts = pycompat.byteskwargs(opts)
2476 opts = pycompat.byteskwargs(opts)
2469 def patchdir(repo):
2477 def patchdir(repo):
2470 """compute a patch repo url from a repo object"""
2478 """compute a patch repo url from a repo object"""
2471 url = repo.url()
2479 url = repo.url()
2472 if url.endswith('/'):
2480 if url.endswith('/'):
2473 url = url[:-1]
2481 url = url[:-1]
2474 return url + '/.hg/patches'
2482 return url + '/.hg/patches'
2475
2483
2476 # main repo (destination and sources)
2484 # main repo (destination and sources)
2477 if dest is None:
2485 if dest is None:
2478 dest = hg.defaultdest(source)
2486 dest = hg.defaultdest(source)
2479 sr = hg.peer(ui, opts, ui.expandpath(source))
2487 sr = hg.peer(ui, opts, ui.expandpath(source))
2480
2488
2481 # patches repo (source only)
2489 # patches repo (source only)
2482 if opts.get('patches'):
2490 if opts.get('patches'):
2483 patchespath = ui.expandpath(opts.get('patches'))
2491 patchespath = ui.expandpath(opts.get('patches'))
2484 else:
2492 else:
2485 patchespath = patchdir(sr)
2493 patchespath = patchdir(sr)
2486 try:
2494 try:
2487 hg.peer(ui, opts, patchespath)
2495 hg.peer(ui, opts, patchespath)
2488 except error.RepoError:
2496 except error.RepoError:
2489 raise error.Abort(_('versioned patch repository not found'
2497 raise error.Abort(_('versioned patch repository not found'
2490 ' (see init --mq)'))
2498 ' (see init --mq)'))
2491 qbase, destrev = None, None
2499 qbase, destrev = None, None
2492 if sr.local():
2500 if sr.local():
2493 repo = sr.local()
2501 repo = sr.local()
2494 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2502 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2495 qbase = repo.mq.applied[0].node
2503 qbase = repo.mq.applied[0].node
2496 if not hg.islocal(dest):
2504 if not hg.islocal(dest):
2497 heads = set(repo.heads())
2505 heads = set(repo.heads())
2498 destrev = list(heads.difference(repo.heads(qbase)))
2506 destrev = list(heads.difference(repo.heads(qbase)))
2499 destrev.append(repo.changelog.parents(qbase)[0])
2507 destrev.append(repo.changelog.parents(qbase)[0])
2500 elif sr.capable('lookup'):
2508 elif sr.capable('lookup'):
2501 try:
2509 try:
2502 qbase = sr.lookup('qbase')
2510 qbase = sr.lookup('qbase')
2503 except error.RepoError:
2511 except error.RepoError:
2504 pass
2512 pass
2505
2513
2506 ui.note(_('cloning main repository\n'))
2514 ui.note(_('cloning main repository\n'))
2507 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2515 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2508 pull=opts.get('pull'),
2516 pull=opts.get('pull'),
2509 revs=destrev,
2517 revs=destrev,
2510 update=False,
2518 update=False,
2511 stream=opts.get('uncompressed'))
2519 stream=opts.get('uncompressed'))
2512
2520
2513 ui.note(_('cloning patch repository\n'))
2521 ui.note(_('cloning patch repository\n'))
2514 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2522 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2515 pull=opts.get('pull'), update=not opts.get('noupdate'),
2523 pull=opts.get('pull'), update=not opts.get('noupdate'),
2516 stream=opts.get('uncompressed'))
2524 stream=opts.get('uncompressed'))
2517
2525
2518 if dr.local():
2526 if dr.local():
2519 repo = dr.local()
2527 repo = dr.local()
2520 if qbase:
2528 if qbase:
2521 ui.note(_('stripping applied patches from destination '
2529 ui.note(_('stripping applied patches from destination '
2522 'repository\n'))
2530 'repository\n'))
2523 strip(ui, repo, [qbase], update=False, backup=None)
2531 strip(ui, repo, [qbase], update=False, backup=None)
2524 if not opts.get('noupdate'):
2532 if not opts.get('noupdate'):
2525 ui.note(_('updating destination repository\n'))
2533 ui.note(_('updating destination repository\n'))
2526 hg.update(repo, repo.changelog.tip())
2534 hg.update(repo, repo.changelog.tip())
2527
2535
2528 @command("qcommit|qci",
2536 @command("qcommit|qci",
2529 commands.table["^commit|ci"][1],
2537 commands.table["^commit|ci"][1],
2530 _('hg qcommit [OPTION]... [FILE]...'),
2538 _('hg qcommit [OPTION]... [FILE]...'),
2531 inferrepo=True)
2539 inferrepo=True)
2532 def commit(ui, repo, *pats, **opts):
2540 def commit(ui, repo, *pats, **opts):
2533 """commit changes in the queue repository (DEPRECATED)
2541 """commit changes in the queue repository (DEPRECATED)
2534
2542
2535 This command is deprecated; use :hg:`commit --mq` instead."""
2543 This command is deprecated; use :hg:`commit --mq` instead."""
2536 q = repo.mq
2544 q = repo.mq
2537 r = q.qrepo()
2545 r = q.qrepo()
2538 if not r:
2546 if not r:
2539 raise error.Abort('no queue repository')
2547 raise error.Abort('no queue repository')
2540 commands.commit(r.ui, r, *pats, **opts)
2548 commands.commit(r.ui, r, *pats, **opts)
2541
2549
2542 @command("qseries",
2550 @command("qseries",
2543 [('m', 'missing', None, _('print patches not in series')),
2551 [('m', 'missing', None, _('print patches not in series')),
2544 ] + seriesopts,
2552 ] + seriesopts,
2545 _('hg qseries [-ms]'))
2553 _('hg qseries [-ms]'))
2546 def series(ui, repo, **opts):
2554 def series(ui, repo, **opts):
2547 """print the entire series file
2555 """print the entire series file
2548
2556
2549 Returns 0 on success."""
2557 Returns 0 on success."""
2550 repo.mq.qseries(repo, missing=opts.get(r'missing'),
2558 repo.mq.qseries(repo, missing=opts.get(r'missing'),
2551 summary=opts.get(r'summary'))
2559 summary=opts.get(r'summary'))
2552 return 0
2560 return 0
2553
2561
2554 @command("qtop", seriesopts, _('hg qtop [-s]'))
2562 @command("qtop", seriesopts, _('hg qtop [-s]'))
2555 def top(ui, repo, **opts):
2563 def top(ui, repo, **opts):
2556 """print the name of the current patch
2564 """print the name of the current patch
2557
2565
2558 Returns 0 on success."""
2566 Returns 0 on success."""
2559 q = repo.mq
2567 q = repo.mq
2560 if q.applied:
2568 if q.applied:
2561 t = q.seriesend(True)
2569 t = q.seriesend(True)
2562 else:
2570 else:
2563 t = 0
2571 t = 0
2564
2572
2565 if t:
2573 if t:
2566 q.qseries(repo, start=t - 1, length=1, status='A',
2574 q.qseries(repo, start=t - 1, length=1, status='A',
2567 summary=opts.get(r'summary'))
2575 summary=opts.get(r'summary'))
2568 else:
2576 else:
2569 ui.write(_("no patches applied\n"))
2577 ui.write(_("no patches applied\n"))
2570 return 1
2578 return 1
2571
2579
2572 @command("qnext", seriesopts, _('hg qnext [-s]'))
2580 @command("qnext", seriesopts, _('hg qnext [-s]'))
2573 def next(ui, repo, **opts):
2581 def next(ui, repo, **opts):
2574 """print the name of the next pushable patch
2582 """print the name of the next pushable patch
2575
2583
2576 Returns 0 on success."""
2584 Returns 0 on success."""
2577 q = repo.mq
2585 q = repo.mq
2578 end = q.seriesend()
2586 end = q.seriesend()
2579 if end == len(q.series):
2587 if end == len(q.series):
2580 ui.write(_("all patches applied\n"))
2588 ui.write(_("all patches applied\n"))
2581 return 1
2589 return 1
2582 q.qseries(repo, start=end, length=1, summary=opts.get(r'summary'))
2590 q.qseries(repo, start=end, length=1, summary=opts.get(r'summary'))
2583
2591
2584 @command("qprev", seriesopts, _('hg qprev [-s]'))
2592 @command("qprev", seriesopts, _('hg qprev [-s]'))
2585 def prev(ui, repo, **opts):
2593 def prev(ui, repo, **opts):
2586 """print the name of the preceding applied patch
2594 """print the name of the preceding applied patch
2587
2595
2588 Returns 0 on success."""
2596 Returns 0 on success."""
2589 q = repo.mq
2597 q = repo.mq
2590 l = len(q.applied)
2598 l = len(q.applied)
2591 if l == 1:
2599 if l == 1:
2592 ui.write(_("only one patch applied\n"))
2600 ui.write(_("only one patch applied\n"))
2593 return 1
2601 return 1
2594 if not l:
2602 if not l:
2595 ui.write(_("no patches applied\n"))
2603 ui.write(_("no patches applied\n"))
2596 return 1
2604 return 1
2597 idx = q.series.index(q.applied[-2].name)
2605 idx = q.series.index(q.applied[-2].name)
2598 q.qseries(repo, start=idx, length=1, status='A',
2606 q.qseries(repo, start=idx, length=1, status='A',
2599 summary=opts.get(r'summary'))
2607 summary=opts.get(r'summary'))
2600
2608
2601 def setupheaderopts(ui, opts):
2609 def setupheaderopts(ui, opts):
2602 if not opts.get('user') and opts.get('currentuser'):
2610 if not opts.get('user') and opts.get('currentuser'):
2603 opts['user'] = ui.username()
2611 opts['user'] = ui.username()
2604 if not opts.get('date') and opts.get('currentdate'):
2612 if not opts.get('date') and opts.get('currentdate'):
2605 opts['date'] = "%d %d" % dateutil.makedate()
2613 opts['date'] = "%d %d" % dateutil.makedate()
2606
2614
2607 @command("^qnew",
2615 @command("^qnew",
2608 [('e', 'edit', None, _('invoke editor on commit messages')),
2616 [('e', 'edit', None, _('invoke editor on commit messages')),
2609 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2617 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2610 ('g', 'git', None, _('use git extended diff format')),
2618 ('g', 'git', None, _('use git extended diff format')),
2611 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2619 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2612 ('u', 'user', '',
2620 ('u', 'user', '',
2613 _('add "From: <USER>" to patch'), _('USER')),
2621 _('add "From: <USER>" to patch'), _('USER')),
2614 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2622 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2615 ('d', 'date', '',
2623 ('d', 'date', '',
2616 _('add "Date: <DATE>" to patch'), _('DATE'))
2624 _('add "Date: <DATE>" to patch'), _('DATE'))
2617 ] + cmdutil.walkopts + cmdutil.commitopts,
2625 ] + cmdutil.walkopts + cmdutil.commitopts,
2618 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'),
2626 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'),
2619 inferrepo=True)
2627 inferrepo=True)
2620 def new(ui, repo, patch, *args, **opts):
2628 def new(ui, repo, patch, *args, **opts):
2621 """create a new patch
2629 """create a new patch
2622
2630
2623 qnew creates a new patch on top of the currently-applied patch (if
2631 qnew creates a new patch on top of the currently-applied patch (if
2624 any). The patch will be initialized with any outstanding changes
2632 any). The patch will be initialized with any outstanding changes
2625 in the working directory. You may also use -I/--include,
2633 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
2634 -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
2635 only changes to matching files to the new patch, leaving the rest
2628 as uncommitted modifications.
2636 as uncommitted modifications.
2629
2637
2630 -u/--user and -d/--date can be used to set the (given) user and
2638 -u/--user and -d/--date can be used to set the (given) user and
2631 date, respectively. -U/--currentuser and -D/--currentdate set user
2639 date, respectively. -U/--currentuser and -D/--currentdate set user
2632 to current user and date to current date.
2640 to current user and date to current date.
2633
2641
2634 -e/--edit, -m/--message or -l/--logfile set the patch header as
2642 -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
2643 well as the commit message. If none is specified, the header is
2636 empty and the commit message is '[mq]: PATCH'.
2644 empty and the commit message is '[mq]: PATCH'.
2637
2645
2638 Use the -g/--git option to keep the patch in the git extended diff
2646 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
2647 format. Read the diffs help topic for more information on why this
2640 is important for preserving permission changes and copy/rename
2648 is important for preserving permission changes and copy/rename
2641 information.
2649 information.
2642
2650
2643 Returns 0 on successful creation of a new patch.
2651 Returns 0 on successful creation of a new patch.
2644 """
2652 """
2645 opts = pycompat.byteskwargs(opts)
2653 opts = pycompat.byteskwargs(opts)
2646 msg = cmdutil.logmessage(ui, opts)
2654 msg = cmdutil.logmessage(ui, opts)
2647 q = repo.mq
2655 q = repo.mq
2648 opts['msg'] = msg
2656 opts['msg'] = msg
2649 setupheaderopts(ui, opts)
2657 setupheaderopts(ui, opts)
2650 q.new(repo, patch, *args, **pycompat.strkwargs(opts))
2658 q.new(repo, patch, *args, **pycompat.strkwargs(opts))
2651 q.savedirty()
2659 q.savedirty()
2652 return 0
2660 return 0
2653
2661
2654 @command("^qrefresh",
2662 @command("^qrefresh",
2655 [('e', 'edit', None, _('invoke editor on commit messages')),
2663 [('e', 'edit', None, _('invoke editor on commit messages')),
2656 ('g', 'git', None, _('use git extended diff format')),
2664 ('g', 'git', None, _('use git extended diff format')),
2657 ('s', 'short', None,
2665 ('s', 'short', None,
2658 _('refresh only files already in the patch and specified files')),
2666 _('refresh only files already in the patch and specified files')),
2659 ('U', 'currentuser', None,
2667 ('U', 'currentuser', None,
2660 _('add/update author field in patch with current user')),
2668 _('add/update author field in patch with current user')),
2661 ('u', 'user', '',
2669 ('u', 'user', '',
2662 _('add/update author field in patch with given user'), _('USER')),
2670 _('add/update author field in patch with given user'), _('USER')),
2663 ('D', 'currentdate', None,
2671 ('D', 'currentdate', None,
2664 _('add/update date field in patch with current date')),
2672 _('add/update date field in patch with current date')),
2665 ('d', 'date', '',
2673 ('d', 'date', '',
2666 _('add/update date field in patch with given date'), _('DATE'))
2674 _('add/update date field in patch with given date'), _('DATE'))
2667 ] + cmdutil.walkopts + cmdutil.commitopts,
2675 ] + cmdutil.walkopts + cmdutil.commitopts,
2668 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'),
2676 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'),
2669 inferrepo=True)
2677 inferrepo=True)
2670 def refresh(ui, repo, *pats, **opts):
2678 def refresh(ui, repo, *pats, **opts):
2671 """update the current patch
2679 """update the current patch
2672
2680
2673 If any file patterns are provided, the refreshed patch will
2681 If any file patterns are provided, the refreshed patch will
2674 contain only the modifications that match those patterns; the
2682 contain only the modifications that match those patterns; the
2675 remaining modifications will remain in the working directory.
2683 remaining modifications will remain in the working directory.
2676
2684
2677 If -s/--short is specified, files currently included in the patch
2685 If -s/--short is specified, files currently included in the patch
2678 will be refreshed just like matched files and remain in the patch.
2686 will be refreshed just like matched files and remain in the patch.
2679
2687
2680 If -e/--edit is specified, Mercurial will start your configured editor for
2688 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
2689 you to enter a message. In case qrefresh fails, you will find a backup of
2682 your message in ``.hg/last-message.txt``.
2690 your message in ``.hg/last-message.txt``.
2683
2691
2684 hg add/remove/copy/rename work as usual, though you might want to
2692 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
2693 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
2694 and renames. See the diffs help topic for more information on the
2687 git diff format.
2695 git diff format.
2688
2696
2689 Returns 0 on success.
2697 Returns 0 on success.
2690 """
2698 """
2691 opts = pycompat.byteskwargs(opts)
2699 opts = pycompat.byteskwargs(opts)
2692 q = repo.mq
2700 q = repo.mq
2693 message = cmdutil.logmessage(ui, opts)
2701 message = cmdutil.logmessage(ui, opts)
2694 setupheaderopts(ui, opts)
2702 setupheaderopts(ui, opts)
2695 with repo.wlock():
2703 with repo.wlock():
2696 ret = q.refresh(repo, pats, msg=message, **pycompat.strkwargs(opts))
2704 ret = q.refresh(repo, pats, msg=message, **pycompat.strkwargs(opts))
2697 q.savedirty()
2705 q.savedirty()
2698 return ret
2706 return ret
2699
2707
2700 @command("^qdiff",
2708 @command("^qdiff",
2701 cmdutil.diffopts + cmdutil.diffopts2 + cmdutil.walkopts,
2709 cmdutil.diffopts + cmdutil.diffopts2 + cmdutil.walkopts,
2702 _('hg qdiff [OPTION]... [FILE]...'),
2710 _('hg qdiff [OPTION]... [FILE]...'),
2703 inferrepo=True)
2711 inferrepo=True)
2704 def diff(ui, repo, *pats, **opts):
2712 def diff(ui, repo, *pats, **opts):
2705 """diff of the current patch and subsequent modifications
2713 """diff of the current patch and subsequent modifications
2706
2714
2707 Shows a diff which includes the current patch as well as any
2715 Shows a diff which includes the current patch as well as any
2708 changes which have been made in the working directory since the
2716 changes which have been made in the working directory since the
2709 last refresh (thus showing what the current patch would become
2717 last refresh (thus showing what the current patch would become
2710 after a qrefresh).
2718 after a qrefresh).
2711
2719
2712 Use :hg:`diff` if you only want to see the changes made since the
2720 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
2721 last qrefresh, or :hg:`export qtip` if you want to see changes
2714 made by the current patch without including changes made since the
2722 made by the current patch without including changes made since the
2715 qrefresh.
2723 qrefresh.
2716
2724
2717 Returns 0 on success.
2725 Returns 0 on success.
2718 """
2726 """
2719 ui.pager('qdiff')
2727 ui.pager('qdiff')
2720 repo.mq.diff(repo, pats, pycompat.byteskwargs(opts))
2728 repo.mq.diff(repo, pats, pycompat.byteskwargs(opts))
2721 return 0
2729 return 0
2722
2730
2723 @command('qfold',
2731 @command('qfold',
2724 [('e', 'edit', None, _('invoke editor on commit messages')),
2732 [('e', 'edit', None, _('invoke editor on commit messages')),
2725 ('k', 'keep', None, _('keep folded patch files')),
2733 ('k', 'keep', None, _('keep folded patch files')),
2726 ] + cmdutil.commitopts,
2734 ] + cmdutil.commitopts,
2727 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2735 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2728 def fold(ui, repo, *files, **opts):
2736 def fold(ui, repo, *files, **opts):
2729 """fold the named patches into the current patch
2737 """fold the named patches into the current patch
2730
2738
2731 Patches must not yet be applied. Each patch will be successively
2739 Patches must not yet be applied. Each patch will be successively
2732 applied to the current patch in the order given. If all the
2740 applied to the current patch in the order given. If all the
2733 patches apply successfully, the current patch will be refreshed
2741 patches apply successfully, the current patch will be refreshed
2734 with the new cumulative patch, and the folded patches will be
2742 with the new cumulative patch, and the folded patches will be
2735 deleted. With -k/--keep, the folded patch files will not be
2743 deleted. With -k/--keep, the folded patch files will not be
2736 removed afterwards.
2744 removed afterwards.
2737
2745
2738 The header for each folded patch will be concatenated with the
2746 The header for each folded patch will be concatenated with the
2739 current patch header, separated by a line of ``* * *``.
2747 current patch header, separated by a line of ``* * *``.
2740
2748
2741 Returns 0 on success."""
2749 Returns 0 on success."""
2742 opts = pycompat.byteskwargs(opts)
2750 opts = pycompat.byteskwargs(opts)
2743 q = repo.mq
2751 q = repo.mq
2744 if not files:
2752 if not files:
2745 raise error.Abort(_('qfold requires at least one patch name'))
2753 raise error.Abort(_('qfold requires at least one patch name'))
2746 if not q.checktoppatch(repo)[0]:
2754 if not q.checktoppatch(repo)[0]:
2747 raise error.Abort(_('no patches applied'))
2755 raise error.Abort(_('no patches applied'))
2748 q.checklocalchanges(repo)
2756 q.checklocalchanges(repo)
2749
2757
2750 message = cmdutil.logmessage(ui, opts)
2758 message = cmdutil.logmessage(ui, opts)
2751
2759
2752 parent = q.lookup('qtip')
2760 parent = q.lookup('qtip')
2753 patches = []
2761 patches = []
2754 messages = []
2762 messages = []
2755 for f in files:
2763 for f in files:
2756 p = q.lookup(f)
2764 p = q.lookup(f)
2757 if p in patches or p == parent:
2765 if p in patches or p == parent:
2758 ui.warn(_('skipping already folded patch %s\n') % p)
2766 ui.warn(_('skipping already folded patch %s\n') % p)
2759 if q.isapplied(p):
2767 if q.isapplied(p):
2760 raise error.Abort(_('qfold cannot fold already applied patch %s')
2768 raise error.Abort(_('qfold cannot fold already applied patch %s')
2761 % p)
2769 % p)
2762 patches.append(p)
2770 patches.append(p)
2763
2771
2764 for p in patches:
2772 for p in patches:
2765 if not message:
2773 if not message:
2766 ph = patchheader(q.join(p), q.plainmode)
2774 ph = patchheader(q.join(p), q.plainmode)
2767 if ph.message:
2775 if ph.message:
2768 messages.append(ph.message)
2776 messages.append(ph.message)
2769 pf = q.join(p)
2777 pf = q.join(p)
2770 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2778 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2771 if not patchsuccess:
2779 if not patchsuccess:
2772 raise error.Abort(_('error folding patch %s') % p)
2780 raise error.Abort(_('error folding patch %s') % p)
2773
2781
2774 if not message:
2782 if not message:
2775 ph = patchheader(q.join(parent), q.plainmode)
2783 ph = patchheader(q.join(parent), q.plainmode)
2776 message = ph.message
2784 message = ph.message
2777 for msg in messages:
2785 for msg in messages:
2778 if msg:
2786 if msg:
2779 if message:
2787 if message:
2780 message.append('* * *')
2788 message.append('* * *')
2781 message.extend(msg)
2789 message.extend(msg)
2782 message = '\n'.join(message)
2790 message = '\n'.join(message)
2783
2791
2784 diffopts = q.patchopts(q.diffopts(), *patches)
2792 diffopts = q.patchopts(q.diffopts(), *patches)
2785 with repo.wlock():
2793 with repo.wlock():
2786 q.refresh(repo, msg=message, git=diffopts.git, edit=opts.get('edit'),
2794 q.refresh(repo, msg=message, git=diffopts.git, edit=opts.get('edit'),
2787 editform='mq.qfold')
2795 editform='mq.qfold')
2788 q.delete(repo, patches, opts)
2796 q.delete(repo, patches, opts)
2789 q.savedirty()
2797 q.savedirty()
2790
2798
2791 @command("qgoto",
2799 @command("qgoto",
2792 [('', 'keep-changes', None,
2800 [('', 'keep-changes', None,
2793 _('tolerate non-conflicting local changes')),
2801 _('tolerate non-conflicting local changes')),
2794 ('f', 'force', None, _('overwrite any local changes')),
2802 ('f', 'force', None, _('overwrite any local changes')),
2795 ('', 'no-backup', None, _('do not save backup copies of files'))],
2803 ('', 'no-backup', None, _('do not save backup copies of files'))],
2796 _('hg qgoto [OPTION]... PATCH'))
2804 _('hg qgoto [OPTION]... PATCH'))
2797 def goto(ui, repo, patch, **opts):
2805 def goto(ui, repo, patch, **opts):
2798 '''push or pop patches until named patch is at top of stack
2806 '''push or pop patches until named patch is at top of stack
2799
2807
2800 Returns 0 on success.'''
2808 Returns 0 on success.'''
2801 opts = pycompat.byteskwargs(opts)
2809 opts = pycompat.byteskwargs(opts)
2802 opts = fixkeepchangesopts(ui, opts)
2810 opts = fixkeepchangesopts(ui, opts)
2803 q = repo.mq
2811 q = repo.mq
2804 patch = q.lookup(patch)
2812 patch = q.lookup(patch)
2805 nobackup = opts.get('no_backup')
2813 nobackup = opts.get('no_backup')
2806 keepchanges = opts.get('keep_changes')
2814 keepchanges = opts.get('keep_changes')
2807 if q.isapplied(patch):
2815 if q.isapplied(patch):
2808 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2816 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2809 keepchanges=keepchanges)
2817 keepchanges=keepchanges)
2810 else:
2818 else:
2811 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2819 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2812 keepchanges=keepchanges)
2820 keepchanges=keepchanges)
2813 q.savedirty()
2821 q.savedirty()
2814 return ret
2822 return ret
2815
2823
2816 @command("qguard",
2824 @command("qguard",
2817 [('l', 'list', None, _('list all patches and guards')),
2825 [('l', 'list', None, _('list all patches and guards')),
2818 ('n', 'none', None, _('drop all guards'))],
2826 ('n', 'none', None, _('drop all guards'))],
2819 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2827 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2820 def guard(ui, repo, *args, **opts):
2828 def guard(ui, repo, *args, **opts):
2821 '''set or print guards for a patch
2829 '''set or print guards for a patch
2822
2830
2823 Guards control whether a patch can be pushed. A patch with no
2831 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
2832 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
2833 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
2834 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2827 has activated it.
2835 has activated it.
2828
2836
2829 With no arguments, print the currently active guards.
2837 With no arguments, print the currently active guards.
2830 With arguments, set guards for the named patch.
2838 With arguments, set guards for the named patch.
2831
2839
2832 .. note::
2840 .. note::
2833
2841
2834 Specifying negative guards now requires '--'.
2842 Specifying negative guards now requires '--'.
2835
2843
2836 To set guards on another patch::
2844 To set guards on another patch::
2837
2845
2838 hg qguard other.patch -- +2.6.17 -stable
2846 hg qguard other.patch -- +2.6.17 -stable
2839
2847
2840 Returns 0 on success.
2848 Returns 0 on success.
2841 '''
2849 '''
2842 def status(idx):
2850 def status(idx):
2843 guards = q.seriesguards[idx] or ['unguarded']
2851 guards = q.seriesguards[idx] or ['unguarded']
2844 if q.series[idx] in applied:
2852 if q.series[idx] in applied:
2845 state = 'applied'
2853 state = 'applied'
2846 elif q.pushable(idx)[0]:
2854 elif q.pushable(idx)[0]:
2847 state = 'unapplied'
2855 state = 'unapplied'
2848 else:
2856 else:
2849 state = 'guarded'
2857 state = 'guarded'
2850 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2858 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2851 ui.write('%s: ' % ui.label(q.series[idx], label))
2859 ui.write('%s: ' % ui.label(q.series[idx], label))
2852
2860
2853 for i, guard in enumerate(guards):
2861 for i, guard in enumerate(guards):
2854 if guard.startswith('+'):
2862 if guard.startswith('+'):
2855 ui.write(guard, label='qguard.positive')
2863 ui.write(guard, label='qguard.positive')
2856 elif guard.startswith('-'):
2864 elif guard.startswith('-'):
2857 ui.write(guard, label='qguard.negative')
2865 ui.write(guard, label='qguard.negative')
2858 else:
2866 else:
2859 ui.write(guard, label='qguard.unguarded')
2867 ui.write(guard, label='qguard.unguarded')
2860 if i != len(guards) - 1:
2868 if i != len(guards) - 1:
2861 ui.write(' ')
2869 ui.write(' ')
2862 ui.write('\n')
2870 ui.write('\n')
2863 q = repo.mq
2871 q = repo.mq
2864 applied = set(p.name for p in q.applied)
2872 applied = set(p.name for p in q.applied)
2865 patch = None
2873 patch = None
2866 args = list(args)
2874 args = list(args)
2867 if opts.get(r'list'):
2875 if opts.get(r'list'):
2868 if args or opts.get('none'):
2876 if args or opts.get('none'):
2869 raise error.Abort(_('cannot mix -l/--list with options or '
2877 raise error.Abort(_('cannot mix -l/--list with options or '
2870 'arguments'))
2878 'arguments'))
2871 for i in xrange(len(q.series)):
2879 for i in xrange(len(q.series)):
2872 status(i)
2880 status(i)
2873 return
2881 return
2874 if not args or args[0][0:1] in '-+':
2882 if not args or args[0][0:1] in '-+':
2875 if not q.applied:
2883 if not q.applied:
2876 raise error.Abort(_('no patches applied'))
2884 raise error.Abort(_('no patches applied'))
2877 patch = q.applied[-1].name
2885 patch = q.applied[-1].name
2878 if patch is None and args[0][0:1] not in '-+':
2886 if patch is None and args[0][0:1] not in '-+':
2879 patch = args.pop(0)
2887 patch = args.pop(0)
2880 if patch is None:
2888 if patch is None:
2881 raise error.Abort(_('no patch to work with'))
2889 raise error.Abort(_('no patch to work with'))
2882 if args or opts.get('none'):
2890 if args or opts.get('none'):
2883 idx = q.findseries(patch)
2891 idx = q.findseries(patch)
2884 if idx is None:
2892 if idx is None:
2885 raise error.Abort(_('no patch named %s') % patch)
2893 raise error.Abort(_('no patch named %s') % patch)
2886 q.setguards(idx, args)
2894 q.setguards(idx, args)
2887 q.savedirty()
2895 q.savedirty()
2888 else:
2896 else:
2889 status(q.series.index(q.lookup(patch)))
2897 status(q.series.index(q.lookup(patch)))
2890
2898
2891 @command("qheader", [], _('hg qheader [PATCH]'))
2899 @command("qheader", [], _('hg qheader [PATCH]'))
2892 def header(ui, repo, patch=None):
2900 def header(ui, repo, patch=None):
2893 """print the header of the topmost or specified patch
2901 """print the header of the topmost or specified patch
2894
2902
2895 Returns 0 on success."""
2903 Returns 0 on success."""
2896 q = repo.mq
2904 q = repo.mq
2897
2905
2898 if patch:
2906 if patch:
2899 patch = q.lookup(patch)
2907 patch = q.lookup(patch)
2900 else:
2908 else:
2901 if not q.applied:
2909 if not q.applied:
2902 ui.write(_('no patches applied\n'))
2910 ui.write(_('no patches applied\n'))
2903 return 1
2911 return 1
2904 patch = q.lookup('qtip')
2912 patch = q.lookup('qtip')
2905 ph = patchheader(q.join(patch), q.plainmode)
2913 ph = patchheader(q.join(patch), q.plainmode)
2906
2914
2907 ui.write('\n'.join(ph.message) + '\n')
2915 ui.write('\n'.join(ph.message) + '\n')
2908
2916
2909 def lastsavename(path):
2917 def lastsavename(path):
2910 (directory, base) = os.path.split(path)
2918 (directory, base) = os.path.split(path)
2911 names = os.listdir(directory)
2919 names = os.listdir(directory)
2912 namere = re.compile("%s.([0-9]+)" % base)
2920 namere = re.compile("%s.([0-9]+)" % base)
2913 maxindex = None
2921 maxindex = None
2914 maxname = None
2922 maxname = None
2915 for f in names:
2923 for f in names:
2916 m = namere.match(f)
2924 m = namere.match(f)
2917 if m:
2925 if m:
2918 index = int(m.group(1))
2926 index = int(m.group(1))
2919 if maxindex is None or index > maxindex:
2927 if maxindex is None or index > maxindex:
2920 maxindex = index
2928 maxindex = index
2921 maxname = f
2929 maxname = f
2922 if maxname:
2930 if maxname:
2923 return (os.path.join(directory, maxname), maxindex)
2931 return (os.path.join(directory, maxname), maxindex)
2924 return (None, None)
2932 return (None, None)
2925
2933
2926 def savename(path):
2934 def savename(path):
2927 (last, index) = lastsavename(path)
2935 (last, index) = lastsavename(path)
2928 if last is None:
2936 if last is None:
2929 index = 0
2937 index = 0
2930 newpath = path + ".%d" % (index + 1)
2938 newpath = path + ".%d" % (index + 1)
2931 return newpath
2939 return newpath
2932
2940
2933 @command("^qpush",
2941 @command("^qpush",
2934 [('', 'keep-changes', None,
2942 [('', 'keep-changes', None,
2935 _('tolerate non-conflicting local changes')),
2943 _('tolerate non-conflicting local changes')),
2936 ('f', 'force', None, _('apply on top of local changes')),
2944 ('f', 'force', None, _('apply on top of local changes')),
2937 ('e', 'exact', None,
2945 ('e', 'exact', None,
2938 _('apply the target patch to its recorded parent')),
2946 _('apply the target patch to its recorded parent')),
2939 ('l', 'list', None, _('list patch name in commit text')),
2947 ('l', 'list', None, _('list patch name in commit text')),
2940 ('a', 'all', None, _('apply all patches')),
2948 ('a', 'all', None, _('apply all patches')),
2941 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2949 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2942 ('n', 'name', '',
2950 ('n', 'name', '',
2943 _('merge queue name (DEPRECATED)'), _('NAME')),
2951 _('merge queue name (DEPRECATED)'), _('NAME')),
2944 ('', 'move', None,
2952 ('', 'move', None,
2945 _('reorder patch series and apply only the patch')),
2953 _('reorder patch series and apply only the patch')),
2946 ('', 'no-backup', None, _('do not save backup copies of files'))],
2954 ('', 'no-backup', None, _('do not save backup copies of files'))],
2947 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2955 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2948 def push(ui, repo, patch=None, **opts):
2956 def push(ui, repo, patch=None, **opts):
2949 """push the next patch onto the stack
2957 """push the next patch onto the stack
2950
2958
2951 By default, abort if the working directory contains uncommitted
2959 By default, abort if the working directory contains uncommitted
2952 changes. With --keep-changes, abort only if the uncommitted files
2960 changes. With --keep-changes, abort only if the uncommitted files
2953 overlap with patched files. With -f/--force, backup and patch over
2961 overlap with patched files. With -f/--force, backup and patch over
2954 uncommitted changes.
2962 uncommitted changes.
2955
2963
2956 Return 0 on success.
2964 Return 0 on success.
2957 """
2965 """
2958 q = repo.mq
2966 q = repo.mq
2959 mergeq = None
2967 mergeq = None
2960
2968
2961 opts = pycompat.byteskwargs(opts)
2969 opts = pycompat.byteskwargs(opts)
2962 opts = fixkeepchangesopts(ui, opts)
2970 opts = fixkeepchangesopts(ui, opts)
2963 if opts.get('merge'):
2971 if opts.get('merge'):
2964 if opts.get('name'):
2972 if opts.get('name'):
2965 newpath = repo.vfs.join(opts.get('name'))
2973 newpath = repo.vfs.join(opts.get('name'))
2966 else:
2974 else:
2967 newpath, i = lastsavename(q.path)
2975 newpath, i = lastsavename(q.path)
2968 if not newpath:
2976 if not newpath:
2969 ui.warn(_("no saved queues found, please use -n\n"))
2977 ui.warn(_("no saved queues found, please use -n\n"))
2970 return 1
2978 return 1
2971 mergeq = queue(ui, repo.baseui, repo.path, newpath)
2979 mergeq = queue(ui, repo.baseui, repo.path, newpath)
2972 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2980 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2973 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2981 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2974 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2982 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2975 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2983 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2976 keepchanges=opts.get('keep_changes'))
2984 keepchanges=opts.get('keep_changes'))
2977 return ret
2985 return ret
2978
2986
2979 @command("^qpop",
2987 @command("^qpop",
2980 [('a', 'all', None, _('pop all patches')),
2988 [('a', 'all', None, _('pop all patches')),
2981 ('n', 'name', '',
2989 ('n', 'name', '',
2982 _('queue name to pop (DEPRECATED)'), _('NAME')),
2990 _('queue name to pop (DEPRECATED)'), _('NAME')),
2983 ('', 'keep-changes', None,
2991 ('', 'keep-changes', None,
2984 _('tolerate non-conflicting local changes')),
2992 _('tolerate non-conflicting local changes')),
2985 ('f', 'force', None, _('forget any local changes to patched files')),
2993 ('f', 'force', None, _('forget any local changes to patched files')),
2986 ('', 'no-backup', None, _('do not save backup copies of files'))],
2994 ('', 'no-backup', None, _('do not save backup copies of files'))],
2987 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2995 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2988 def pop(ui, repo, patch=None, **opts):
2996 def pop(ui, repo, patch=None, **opts):
2989 """pop the current patch off the stack
2997 """pop the current patch off the stack
2990
2998
2991 Without argument, pops off the top of the patch stack. If given a
2999 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
3000 patch name, keeps popping off patches until the named patch is at
2993 the top of the stack.
3001 the top of the stack.
2994
3002
2995 By default, abort if the working directory contains uncommitted
3003 By default, abort if the working directory contains uncommitted
2996 changes. With --keep-changes, abort only if the uncommitted files
3004 changes. With --keep-changes, abort only if the uncommitted files
2997 overlap with patched files. With -f/--force, backup and discard
3005 overlap with patched files. With -f/--force, backup and discard
2998 changes made to such files.
3006 changes made to such files.
2999
3007
3000 Return 0 on success.
3008 Return 0 on success.
3001 """
3009 """
3002 opts = pycompat.byteskwargs(opts)
3010 opts = pycompat.byteskwargs(opts)
3003 opts = fixkeepchangesopts(ui, opts)
3011 opts = fixkeepchangesopts(ui, opts)
3004 localupdate = True
3012 localupdate = True
3005 if opts.get('name'):
3013 if opts.get('name'):
3006 q = queue(ui, repo.baseui, repo.path, repo.vfs.join(opts.get('name')))
3014 q = queue(ui, repo.baseui, repo.path, repo.vfs.join(opts.get('name')))
3007 ui.warn(_('using patch queue: %s\n') % q.path)
3015 ui.warn(_('using patch queue: %s\n') % q.path)
3008 localupdate = False
3016 localupdate = False
3009 else:
3017 else:
3010 q = repo.mq
3018 q = repo.mq
3011 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
3019 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
3012 all=opts.get('all'), nobackup=opts.get('no_backup'),
3020 all=opts.get('all'), nobackup=opts.get('no_backup'),
3013 keepchanges=opts.get('keep_changes'))
3021 keepchanges=opts.get('keep_changes'))
3014 q.savedirty()
3022 q.savedirty()
3015 return ret
3023 return ret
3016
3024
3017 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
3025 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
3018 def rename(ui, repo, patch, name=None, **opts):
3026 def rename(ui, repo, patch, name=None, **opts):
3019 """rename a patch
3027 """rename a patch
3020
3028
3021 With one argument, renames the current patch to PATCH1.
3029 With one argument, renames the current patch to PATCH1.
3022 With two arguments, renames PATCH1 to PATCH2.
3030 With two arguments, renames PATCH1 to PATCH2.
3023
3031
3024 Returns 0 on success."""
3032 Returns 0 on success."""
3025 q = repo.mq
3033 q = repo.mq
3026 if not name:
3034 if not name:
3027 name = patch
3035 name = patch
3028 patch = None
3036 patch = None
3029
3037
3030 if patch:
3038 if patch:
3031 patch = q.lookup(patch)
3039 patch = q.lookup(patch)
3032 else:
3040 else:
3033 if not q.applied:
3041 if not q.applied:
3034 ui.write(_('no patches applied\n'))
3042 ui.write(_('no patches applied\n'))
3035 return
3043 return
3036 patch = q.lookup('qtip')
3044 patch = q.lookup('qtip')
3037 absdest = q.join(name)
3045 absdest = q.join(name)
3038 if os.path.isdir(absdest):
3046 if os.path.isdir(absdest):
3039 name = normname(os.path.join(name, os.path.basename(patch)))
3047 name = normname(os.path.join(name, os.path.basename(patch)))
3040 absdest = q.join(name)
3048 absdest = q.join(name)
3041 q.checkpatchname(name)
3049 q.checkpatchname(name)
3042
3050
3043 ui.note(_('renaming %s to %s\n') % (patch, name))
3051 ui.note(_('renaming %s to %s\n') % (patch, name))
3044 i = q.findseries(patch)
3052 i = q.findseries(patch)
3045 guards = q.guard_re.findall(q.fullseries[i])
3053 guards = q.guard_re.findall(q.fullseries[i])
3046 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
3054 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
3047 q.parseseries()
3055 q.parseseries()
3048 q.seriesdirty = True
3056 q.seriesdirty = True
3049
3057
3050 info = q.isapplied(patch)
3058 info = q.isapplied(patch)
3051 if info:
3059 if info:
3052 q.applied[info[0]] = statusentry(info[1], name)
3060 q.applied[info[0]] = statusentry(info[1], name)
3053 q.applieddirty = True
3061 q.applieddirty = True
3054
3062
3055 destdir = os.path.dirname(absdest)
3063 destdir = os.path.dirname(absdest)
3056 if not os.path.isdir(destdir):
3064 if not os.path.isdir(destdir):
3057 os.makedirs(destdir)
3065 os.makedirs(destdir)
3058 util.rename(q.join(patch), absdest)
3066 util.rename(q.join(patch), absdest)
3059 r = q.qrepo()
3067 r = q.qrepo()
3060 if r and patch in r.dirstate:
3068 if r and patch in r.dirstate:
3061 wctx = r[None]
3069 wctx = r[None]
3062 with r.wlock():
3070 with r.wlock():
3063 if r.dirstate[patch] == 'a':
3071 if r.dirstate[patch] == 'a':
3064 r.dirstate.drop(patch)
3072 r.dirstate.drop(patch)
3065 r.dirstate.add(name)
3073 r.dirstate.add(name)
3066 else:
3074 else:
3067 wctx.copy(patch, name)
3075 wctx.copy(patch, name)
3068 wctx.forget([patch])
3076 wctx.forget([patch])
3069
3077
3070 q.savedirty()
3078 q.savedirty()
3071
3079
3072 @command("qrestore",
3080 @command("qrestore",
3073 [('d', 'delete', None, _('delete save entry')),
3081 [('d', 'delete', None, _('delete save entry')),
3074 ('u', 'update', None, _('update queue working directory'))],
3082 ('u', 'update', None, _('update queue working directory'))],
3075 _('hg qrestore [-d] [-u] REV'))
3083 _('hg qrestore [-d] [-u] REV'))
3076 def restore(ui, repo, rev, **opts):
3084 def restore(ui, repo, rev, **opts):
3077 """restore the queue state saved by a revision (DEPRECATED)
3085 """restore the queue state saved by a revision (DEPRECATED)
3078
3086
3079 This command is deprecated, use :hg:`rebase` instead."""
3087 This command is deprecated, use :hg:`rebase` instead."""
3080 rev = repo.lookup(rev)
3088 rev = repo.lookup(rev)
3081 q = repo.mq
3089 q = repo.mq
3082 q.restore(repo, rev, delete=opts.get(r'delete'),
3090 q.restore(repo, rev, delete=opts.get(r'delete'),
3083 qupdate=opts.get(r'update'))
3091 qupdate=opts.get(r'update'))
3084 q.savedirty()
3092 q.savedirty()
3085 return 0
3093 return 0
3086
3094
3087 @command("qsave",
3095 @command("qsave",
3088 [('c', 'copy', None, _('copy patch directory')),
3096 [('c', 'copy', None, _('copy patch directory')),
3089 ('n', 'name', '',
3097 ('n', 'name', '',
3090 _('copy directory name'), _('NAME')),
3098 _('copy directory name'), _('NAME')),
3091 ('e', 'empty', None, _('clear queue status file')),
3099 ('e', 'empty', None, _('clear queue status file')),
3092 ('f', 'force', None, _('force copy'))] + cmdutil.commitopts,
3100 ('f', 'force', None, _('force copy'))] + cmdutil.commitopts,
3093 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
3101 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
3094 def save(ui, repo, **opts):
3102 def save(ui, repo, **opts):
3095 """save current queue state (DEPRECATED)
3103 """save current queue state (DEPRECATED)
3096
3104
3097 This command is deprecated, use :hg:`rebase` instead."""
3105 This command is deprecated, use :hg:`rebase` instead."""
3098 q = repo.mq
3106 q = repo.mq
3099 opts = pycompat.byteskwargs(opts)
3107 opts = pycompat.byteskwargs(opts)
3100 message = cmdutil.logmessage(ui, opts)
3108 message = cmdutil.logmessage(ui, opts)
3101 ret = q.save(repo, msg=message)
3109 ret = q.save(repo, msg=message)
3102 if ret:
3110 if ret:
3103 return ret
3111 return ret
3104 q.savedirty() # save to .hg/patches before copying
3112 q.savedirty() # save to .hg/patches before copying
3105 if opts.get('copy'):
3113 if opts.get('copy'):
3106 path = q.path
3114 path = q.path
3107 if opts.get('name'):
3115 if opts.get('name'):
3108 newpath = os.path.join(q.basepath, opts.get('name'))
3116 newpath = os.path.join(q.basepath, opts.get('name'))
3109 if os.path.exists(newpath):
3117 if os.path.exists(newpath):
3110 if not os.path.isdir(newpath):
3118 if not os.path.isdir(newpath):
3111 raise error.Abort(_('destination %s exists and is not '
3119 raise error.Abort(_('destination %s exists and is not '
3112 'a directory') % newpath)
3120 'a directory') % newpath)
3113 if not opts.get('force'):
3121 if not opts.get('force'):
3114 raise error.Abort(_('destination %s exists, '
3122 raise error.Abort(_('destination %s exists, '
3115 'use -f to force') % newpath)
3123 'use -f to force') % newpath)
3116 else:
3124 else:
3117 newpath = savename(path)
3125 newpath = savename(path)
3118 ui.warn(_("copy %s to %s\n") % (path, newpath))
3126 ui.warn(_("copy %s to %s\n") % (path, newpath))
3119 util.copyfiles(path, newpath)
3127 util.copyfiles(path, newpath)
3120 if opts.get('empty'):
3128 if opts.get('empty'):
3121 del q.applied[:]
3129 del q.applied[:]
3122 q.applieddirty = True
3130 q.applieddirty = True
3123 q.savedirty()
3131 q.savedirty()
3124 return 0
3132 return 0
3125
3133
3126
3134
3127 @command("qselect",
3135 @command("qselect",
3128 [('n', 'none', None, _('disable all guards')),
3136 [('n', 'none', None, _('disable all guards')),
3129 ('s', 'series', None, _('list all guards in series file')),
3137 ('s', 'series', None, _('list all guards in series file')),
3130 ('', 'pop', None, _('pop to before first guarded applied patch')),
3138 ('', 'pop', None, _('pop to before first guarded applied patch')),
3131 ('', 'reapply', None, _('pop, then reapply patches'))],
3139 ('', 'reapply', None, _('pop, then reapply patches'))],
3132 _('hg qselect [OPTION]... [GUARD]...'))
3140 _('hg qselect [OPTION]... [GUARD]...'))
3133 def select(ui, repo, *args, **opts):
3141 def select(ui, repo, *args, **opts):
3134 '''set or print guarded patches to push
3142 '''set or print guarded patches to push
3135
3143
3136 Use the :hg:`qguard` command to set or print guards on patch, then use
3144 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
3145 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
3146 it has no guards or any positive guards match the currently
3139 selected guard, but will not be pushed if any negative guards
3147 selected guard, but will not be pushed if any negative guards
3140 match the current guard. For example::
3148 match the current guard. For example::
3141
3149
3142 qguard foo.patch -- -stable (negative guard)
3150 qguard foo.patch -- -stable (negative guard)
3143 qguard bar.patch +stable (positive guard)
3151 qguard bar.patch +stable (positive guard)
3144 qselect stable
3152 qselect stable
3145
3153
3146 This activates the "stable" guard. mq will skip foo.patch (because
3154 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
3155 it has a negative match) but push bar.patch (because it has a
3148 positive match).
3156 positive match).
3149
3157
3150 With no arguments, prints the currently active guards.
3158 With no arguments, prints the currently active guards.
3151 With one argument, sets the active guard.
3159 With one argument, sets the active guard.
3152
3160
3153 Use -n/--none to deactivate guards (no other arguments needed).
3161 Use -n/--none to deactivate guards (no other arguments needed).
3154 When no guards are active, patches with positive guards are
3162 When no guards are active, patches with positive guards are
3155 skipped and patches with negative guards are pushed.
3163 skipped and patches with negative guards are pushed.
3156
3164
3157 qselect can change the guards on applied patches. It does not pop
3165 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
3166 guarded patches by default. Use --pop to pop back to the last
3159 applied patch that is not guarded. Use --reapply (which implies
3167 applied patch that is not guarded. Use --reapply (which implies
3160 --pop) to push back to the current patch afterwards, but skip
3168 --pop) to push back to the current patch afterwards, but skip
3161 guarded patches.
3169 guarded patches.
3162
3170
3163 Use -s/--series to print a list of all guards in the series file
3171 Use -s/--series to print a list of all guards in the series file
3164 (no other arguments needed). Use -v for more information.
3172 (no other arguments needed). Use -v for more information.
3165
3173
3166 Returns 0 on success.'''
3174 Returns 0 on success.'''
3167
3175
3168 q = repo.mq
3176 q = repo.mq
3169 opts = pycompat.byteskwargs(opts)
3177 opts = pycompat.byteskwargs(opts)
3170 guards = q.active()
3178 guards = q.active()
3171 pushable = lambda i: q.pushable(q.applied[i].name)[0]
3179 pushable = lambda i: q.pushable(q.applied[i].name)[0]
3172 if args or opts.get('none'):
3180 if args or opts.get('none'):
3173 old_unapplied = q.unapplied(repo)
3181 old_unapplied = q.unapplied(repo)
3174 old_guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
3182 old_guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
3175 q.setactive(args)
3183 q.setactive(args)
3176 q.savedirty()
3184 q.savedirty()
3177 if not args:
3185 if not args:
3178 ui.status(_('guards deactivated\n'))
3186 ui.status(_('guards deactivated\n'))
3179 if not opts.get('pop') and not opts.get('reapply'):
3187 if not opts.get('pop') and not opts.get('reapply'):
3180 unapplied = q.unapplied(repo)
3188 unapplied = q.unapplied(repo)
3181 guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
3189 guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
3182 if len(unapplied) != len(old_unapplied):
3190 if len(unapplied) != len(old_unapplied):
3183 ui.status(_('number of unguarded, unapplied patches has '
3191 ui.status(_('number of unguarded, unapplied patches has '
3184 'changed from %d to %d\n') %
3192 'changed from %d to %d\n') %
3185 (len(old_unapplied), len(unapplied)))
3193 (len(old_unapplied), len(unapplied)))
3186 if len(guarded) != len(old_guarded):
3194 if len(guarded) != len(old_guarded):
3187 ui.status(_('number of guarded, applied patches has changed '
3195 ui.status(_('number of guarded, applied patches has changed '
3188 'from %d to %d\n') %
3196 'from %d to %d\n') %
3189 (len(old_guarded), len(guarded)))
3197 (len(old_guarded), len(guarded)))
3190 elif opts.get('series'):
3198 elif opts.get('series'):
3191 guards = {}
3199 guards = {}
3192 noguards = 0
3200 noguards = 0
3193 for gs in q.seriesguards:
3201 for gs in q.seriesguards:
3194 if not gs:
3202 if not gs:
3195 noguards += 1
3203 noguards += 1
3196 for g in gs:
3204 for g in gs:
3197 guards.setdefault(g, 0)
3205 guards.setdefault(g, 0)
3198 guards[g] += 1
3206 guards[g] += 1
3199 if ui.verbose:
3207 if ui.verbose:
3200 guards['NONE'] = noguards
3208 guards['NONE'] = noguards
3201 guards = list(guards.items())
3209 guards = list(guards.items())
3202 guards.sort(key=lambda x: x[0][1:])
3210 guards.sort(key=lambda x: x[0][1:])
3203 if guards:
3211 if guards:
3204 ui.note(_('guards in series file:\n'))
3212 ui.note(_('guards in series file:\n'))
3205 for guard, count in guards:
3213 for guard, count in guards:
3206 ui.note('%2d ' % count)
3214 ui.note('%2d ' % count)
3207 ui.write(guard, '\n')
3215 ui.write(guard, '\n')
3208 else:
3216 else:
3209 ui.note(_('no guards in series file\n'))
3217 ui.note(_('no guards in series file\n'))
3210 else:
3218 else:
3211 if guards:
3219 if guards:
3212 ui.note(_('active guards:\n'))
3220 ui.note(_('active guards:\n'))
3213 for g in guards:
3221 for g in guards:
3214 ui.write(g, '\n')
3222 ui.write(g, '\n')
3215 else:
3223 else:
3216 ui.write(_('no active guards\n'))
3224 ui.write(_('no active guards\n'))
3217 reapply = opts.get('reapply') and q.applied and q.applied[-1].name
3225 reapply = opts.get('reapply') and q.applied and q.applied[-1].name
3218 popped = False
3226 popped = False
3219 if opts.get('pop') or opts.get('reapply'):
3227 if opts.get('pop') or opts.get('reapply'):
3220 for i in xrange(len(q.applied)):
3228 for i in xrange(len(q.applied)):
3221 if not pushable(i):
3229 if not pushable(i):
3222 ui.status(_('popping guarded patches\n'))
3230 ui.status(_('popping guarded patches\n'))
3223 popped = True
3231 popped = True
3224 if i == 0:
3232 if i == 0:
3225 q.pop(repo, all=True)
3233 q.pop(repo, all=True)
3226 else:
3234 else:
3227 q.pop(repo, q.applied[i - 1].name)
3235 q.pop(repo, q.applied[i - 1].name)
3228 break
3236 break
3229 if popped:
3237 if popped:
3230 try:
3238 try:
3231 if reapply:
3239 if reapply:
3232 ui.status(_('reapplying unguarded patches\n'))
3240 ui.status(_('reapplying unguarded patches\n'))
3233 q.push(repo, reapply)
3241 q.push(repo, reapply)
3234 finally:
3242 finally:
3235 q.savedirty()
3243 q.savedirty()
3236
3244
3237 @command("qfinish",
3245 @command("qfinish",
3238 [('a', 'applied', None, _('finish all applied changesets'))],
3246 [('a', 'applied', None, _('finish all applied changesets'))],
3239 _('hg qfinish [-a] [REV]...'))
3247 _('hg qfinish [-a] [REV]...'))
3240 def finish(ui, repo, *revrange, **opts):
3248 def finish(ui, repo, *revrange, **opts):
3241 """move applied patches into repository history
3249 """move applied patches into repository history
3242
3250
3243 Finishes the specified revisions (corresponding to applied
3251 Finishes the specified revisions (corresponding to applied
3244 patches) by moving them out of mq control into regular repository
3252 patches) by moving them out of mq control into regular repository
3245 history.
3253 history.
3246
3254
3247 Accepts a revision range or the -a/--applied option. If --applied
3255 Accepts a revision range or the -a/--applied option. If --applied
3248 is specified, all applied mq revisions are removed from mq
3256 is specified, all applied mq revisions are removed from mq
3249 control. Otherwise, the given revisions must be at the base of the
3257 control. Otherwise, the given revisions must be at the base of the
3250 stack of applied patches.
3258 stack of applied patches.
3251
3259
3252 This can be especially useful if your changes have been applied to
3260 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
3261 an upstream repository, or if you are about to push your changes
3254 to upstream.
3262 to upstream.
3255
3263
3256 Returns 0 on success.
3264 Returns 0 on success.
3257 """
3265 """
3258 if not opts.get(r'applied') and not revrange:
3266 if not opts.get(r'applied') and not revrange:
3259 raise error.Abort(_('no revisions specified'))
3267 raise error.Abort(_('no revisions specified'))
3260 elif opts.get(r'applied'):
3268 elif opts.get(r'applied'):
3261 revrange = ('qbase::qtip',) + revrange
3269 revrange = ('qbase::qtip',) + revrange
3262
3270
3263 q = repo.mq
3271 q = repo.mq
3264 if not q.applied:
3272 if not q.applied:
3265 ui.status(_('no patches applied\n'))
3273 ui.status(_('no patches applied\n'))
3266 return 0
3274 return 0
3267
3275
3268 revs = scmutil.revrange(repo, revrange)
3276 revs = scmutil.revrange(repo, revrange)
3269 if repo['.'].rev() in revs and repo[None].files():
3277 if repo['.'].rev() in revs and repo[None].files():
3270 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3278 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3271 # queue.finish may changes phases but leave the responsibility to lock the
3279 # 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
3280 # repo to the caller to avoid deadlock with wlock. This command code is
3273 # responsibility for this locking.
3281 # responsibility for this locking.
3274 with repo.lock():
3282 with repo.lock():
3275 q.finish(repo, revs)
3283 q.finish(repo, revs)
3276 q.savedirty()
3284 q.savedirty()
3277 return 0
3285 return 0
3278
3286
3279 @command("qqueue",
3287 @command("qqueue",
3280 [('l', 'list', False, _('list all available queues')),
3288 [('l', 'list', False, _('list all available queues')),
3281 ('', 'active', False, _('print name of active queue')),
3289 ('', 'active', False, _('print name of active queue')),
3282 ('c', 'create', False, _('create new queue')),
3290 ('c', 'create', False, _('create new queue')),
3283 ('', 'rename', False, _('rename active queue')),
3291 ('', 'rename', False, _('rename active queue')),
3284 ('', 'delete', False, _('delete reference to queue')),
3292 ('', 'delete', False, _('delete reference to queue')),
3285 ('', 'purge', False, _('delete queue, and remove patch dir')),
3293 ('', 'purge', False, _('delete queue, and remove patch dir')),
3286 ],
3294 ],
3287 _('[OPTION] [QUEUE]'))
3295 _('[OPTION] [QUEUE]'))
3288 def qqueue(ui, repo, name=None, **opts):
3296 def qqueue(ui, repo, name=None, **opts):
3289 '''manage multiple patch queues
3297 '''manage multiple patch queues
3290
3298
3291 Supports switching between different patch queues, as well as creating
3299 Supports switching between different patch queues, as well as creating
3292 new patch queues and deleting existing ones.
3300 new patch queues and deleting existing ones.
3293
3301
3294 Omitting a queue name or specifying -l/--list will show you the registered
3302 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
3303 queues - by default the "normal" patches queue is registered. The currently
3296 active queue will be marked with "(active)". Specifying --active will print
3304 active queue will be marked with "(active)". Specifying --active will print
3297 only the name of the active queue.
3305 only the name of the active queue.
3298
3306
3299 To create a new queue, use -c/--create. The queue is automatically made
3307 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
3308 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
3309 currently active queue in the repository. Then the queue will only be
3302 created and switching will fail.
3310 created and switching will fail.
3303
3311
3304 To delete an existing queue, use --delete. You cannot delete the currently
3312 To delete an existing queue, use --delete. You cannot delete the currently
3305 active queue.
3313 active queue.
3306
3314
3307 Returns 0 on success.
3315 Returns 0 on success.
3308 '''
3316 '''
3309 q = repo.mq
3317 q = repo.mq
3310 _defaultqueue = 'patches'
3318 _defaultqueue = 'patches'
3311 _allqueues = 'patches.queues'
3319 _allqueues = 'patches.queues'
3312 _activequeue = 'patches.queue'
3320 _activequeue = 'patches.queue'
3313
3321
3314 def _getcurrent():
3322 def _getcurrent():
3315 cur = os.path.basename(q.path)
3323 cur = os.path.basename(q.path)
3316 if cur.startswith('patches-'):
3324 if cur.startswith('patches-'):
3317 cur = cur[8:]
3325 cur = cur[8:]
3318 return cur
3326 return cur
3319
3327
3320 def _noqueues():
3328 def _noqueues():
3321 try:
3329 try:
3322 fh = repo.vfs(_allqueues, 'r')
3330 fh = repo.vfs(_allqueues, 'r')
3323 fh.close()
3331 fh.close()
3324 except IOError:
3332 except IOError:
3325 return True
3333 return True
3326
3334
3327 return False
3335 return False
3328
3336
3329 def _getqueues():
3337 def _getqueues():
3330 current = _getcurrent()
3338 current = _getcurrent()
3331
3339
3332 try:
3340 try:
3333 fh = repo.vfs(_allqueues, 'r')
3341 fh = repo.vfs(_allqueues, 'r')
3334 queues = [queue.strip() for queue in fh if queue.strip()]
3342 queues = [queue.strip() for queue in fh if queue.strip()]
3335 fh.close()
3343 fh.close()
3336 if current not in queues:
3344 if current not in queues:
3337 queues.append(current)
3345 queues.append(current)
3338 except IOError:
3346 except IOError:
3339 queues = [_defaultqueue]
3347 queues = [_defaultqueue]
3340
3348
3341 return sorted(queues)
3349 return sorted(queues)
3342
3350
3343 def _setactive(name):
3351 def _setactive(name):
3344 if q.applied:
3352 if q.applied:
3345 raise error.Abort(_('new queue created, but cannot make active '
3353 raise error.Abort(_('new queue created, but cannot make active '
3346 'as patches are applied'))
3354 'as patches are applied'))
3347 _setactivenocheck(name)
3355 _setactivenocheck(name)
3348
3356
3349 def _setactivenocheck(name):
3357 def _setactivenocheck(name):
3350 fh = repo.vfs(_activequeue, 'w')
3358 fh = repo.vfs(_activequeue, 'w')
3351 if name != 'patches':
3359 if name != 'patches':
3352 fh.write(name)
3360 fh.write(name)
3353 fh.close()
3361 fh.close()
3354
3362
3355 def _addqueue(name):
3363 def _addqueue(name):
3356 fh = repo.vfs(_allqueues, 'a')
3364 fh = repo.vfs(_allqueues, 'a')
3357 fh.write('%s\n' % (name,))
3365 fh.write('%s\n' % (name,))
3358 fh.close()
3366 fh.close()
3359
3367
3360 def _queuedir(name):
3368 def _queuedir(name):
3361 if name == 'patches':
3369 if name == 'patches':
3362 return repo.vfs.join('patches')
3370 return repo.vfs.join('patches')
3363 else:
3371 else:
3364 return repo.vfs.join('patches-' + name)
3372 return repo.vfs.join('patches-' + name)
3365
3373
3366 def _validname(name):
3374 def _validname(name):
3367 for n in name:
3375 for n in name:
3368 if n in ':\\/.':
3376 if n in ':\\/.':
3369 return False
3377 return False
3370 return True
3378 return True
3371
3379
3372 def _delete(name):
3380 def _delete(name):
3373 if name not in existing:
3381 if name not in existing:
3374 raise error.Abort(_('cannot delete queue that does not exist'))
3382 raise error.Abort(_('cannot delete queue that does not exist'))
3375
3383
3376 current = _getcurrent()
3384 current = _getcurrent()
3377
3385
3378 if name == current:
3386 if name == current:
3379 raise error.Abort(_('cannot delete currently active queue'))
3387 raise error.Abort(_('cannot delete currently active queue'))
3380
3388
3381 fh = repo.vfs('patches.queues.new', 'w')
3389 fh = repo.vfs('patches.queues.new', 'w')
3382 for queue in existing:
3390 for queue in existing:
3383 if queue == name:
3391 if queue == name:
3384 continue
3392 continue
3385 fh.write('%s\n' % (queue,))
3393 fh.write('%s\n' % (queue,))
3386 fh.close()
3394 fh.close()
3387 repo.vfs.rename('patches.queues.new', _allqueues)
3395 repo.vfs.rename('patches.queues.new', _allqueues)
3388
3396
3389 opts = pycompat.byteskwargs(opts)
3397 opts = pycompat.byteskwargs(opts)
3390 if not name or opts.get('list') or opts.get('active'):
3398 if not name or opts.get('list') or opts.get('active'):
3391 current = _getcurrent()
3399 current = _getcurrent()
3392 if opts.get('active'):
3400 if opts.get('active'):
3393 ui.write('%s\n' % (current,))
3401 ui.write('%s\n' % (current,))
3394 return
3402 return
3395 for queue in _getqueues():
3403 for queue in _getqueues():
3396 ui.write('%s' % (queue,))
3404 ui.write('%s' % (queue,))
3397 if queue == current and not ui.quiet:
3405 if queue == current and not ui.quiet:
3398 ui.write(_(' (active)\n'))
3406 ui.write(_(' (active)\n'))
3399 else:
3407 else:
3400 ui.write('\n')
3408 ui.write('\n')
3401 return
3409 return
3402
3410
3403 if not _validname(name):
3411 if not _validname(name):
3404 raise error.Abort(
3412 raise error.Abort(
3405 _('invalid queue name, may not contain the characters ":\\/."'))
3413 _('invalid queue name, may not contain the characters ":\\/."'))
3406
3414
3407 with repo.wlock():
3415 with repo.wlock():
3408 existing = _getqueues()
3416 existing = _getqueues()
3409
3417
3410 if opts.get('create'):
3418 if opts.get('create'):
3411 if name in existing:
3419 if name in existing:
3412 raise error.Abort(_('queue "%s" already exists') % name)
3420 raise error.Abort(_('queue "%s" already exists') % name)
3413 if _noqueues():
3421 if _noqueues():
3414 _addqueue(_defaultqueue)
3422 _addqueue(_defaultqueue)
3415 _addqueue(name)
3423 _addqueue(name)
3416 _setactive(name)
3424 _setactive(name)
3417 elif opts.get('rename'):
3425 elif opts.get('rename'):
3418 current = _getcurrent()
3426 current = _getcurrent()
3419 if name == current:
3427 if name == current:
3420 raise error.Abort(_('can\'t rename "%s" to its current name')
3428 raise error.Abort(_('can\'t rename "%s" to its current name')
3421 % name)
3429 % name)
3422 if name in existing:
3430 if name in existing:
3423 raise error.Abort(_('queue "%s" already exists') % name)
3431 raise error.Abort(_('queue "%s" already exists') % name)
3424
3432
3425 olddir = _queuedir(current)
3433 olddir = _queuedir(current)
3426 newdir = _queuedir(name)
3434 newdir = _queuedir(name)
3427
3435
3428 if os.path.exists(newdir):
3436 if os.path.exists(newdir):
3429 raise error.Abort(_('non-queue directory "%s" already exists') %
3437 raise error.Abort(_('non-queue directory "%s" already exists') %
3430 newdir)
3438 newdir)
3431
3439
3432 fh = repo.vfs('patches.queues.new', 'w')
3440 fh = repo.vfs('patches.queues.new', 'w')
3433 for queue in existing:
3441 for queue in existing:
3434 if queue == current:
3442 if queue == current:
3435 fh.write('%s\n' % (name,))
3443 fh.write('%s\n' % (name,))
3436 if os.path.exists(olddir):
3444 if os.path.exists(olddir):
3437 util.rename(olddir, newdir)
3445 util.rename(olddir, newdir)
3438 else:
3446 else:
3439 fh.write('%s\n' % (queue,))
3447 fh.write('%s\n' % (queue,))
3440 fh.close()
3448 fh.close()
3441 repo.vfs.rename('patches.queues.new', _allqueues)
3449 repo.vfs.rename('patches.queues.new', _allqueues)
3442 _setactivenocheck(name)
3450 _setactivenocheck(name)
3443 elif opts.get('delete'):
3451 elif opts.get('delete'):
3444 _delete(name)
3452 _delete(name)
3445 elif opts.get('purge'):
3453 elif opts.get('purge'):
3446 if name in existing:
3454 if name in existing:
3447 _delete(name)
3455 _delete(name)
3448 qdir = _queuedir(name)
3456 qdir = _queuedir(name)
3449 if os.path.exists(qdir):
3457 if os.path.exists(qdir):
3450 shutil.rmtree(qdir)
3458 shutil.rmtree(qdir)
3451 else:
3459 else:
3452 if name not in existing:
3460 if name not in existing:
3453 raise error.Abort(_('use --create to create a new queue'))
3461 raise error.Abort(_('use --create to create a new queue'))
3454 _setactive(name)
3462 _setactive(name)
3455
3463
3456 def mqphasedefaults(repo, roots):
3464 def mqphasedefaults(repo, roots):
3457 """callback used to set mq changeset as secret when no phase data exists"""
3465 """callback used to set mq changeset as secret when no phase data exists"""
3458 if repo.mq.applied:
3466 if repo.mq.applied:
3459 if repo.ui.configbool('mq', 'secret'):
3467 if repo.ui.configbool('mq', 'secret'):
3460 mqphase = phases.secret
3468 mqphase = phases.secret
3461 else:
3469 else:
3462 mqphase = phases.draft
3470 mqphase = phases.draft
3463 qbase = repo[repo.mq.applied[0].node]
3471 qbase = repo[repo.mq.applied[0].node]
3464 roots[mqphase].add(qbase.node())
3472 roots[mqphase].add(qbase.node())
3465 return roots
3473 return roots
3466
3474
3467 def reposetup(ui, repo):
3475 def reposetup(ui, repo):
3468 class mqrepo(repo.__class__):
3476 class mqrepo(repo.__class__):
3469 @localrepo.unfilteredpropertycache
3477 @localrepo.unfilteredpropertycache
3470 def mq(self):
3478 def mq(self):
3471 return queue(self.ui, self.baseui, self.path)
3479 return queue(self.ui, self.baseui, self.path)
3472
3480
3473 def invalidateall(self):
3481 def invalidateall(self):
3474 super(mqrepo, self).invalidateall()
3482 super(mqrepo, self).invalidateall()
3475 if localrepo.hasunfilteredcache(self, 'mq'):
3483 if localrepo.hasunfilteredcache(self, 'mq'):
3476 # recreate mq in case queue path was changed
3484 # recreate mq in case queue path was changed
3477 delattr(self.unfiltered(), 'mq')
3485 delattr(self.unfiltered(), 'mq')
3478
3486
3479 def abortifwdirpatched(self, errmsg, force=False):
3487 def abortifwdirpatched(self, errmsg, force=False):
3480 if self.mq.applied and self.mq.checkapplied and not force:
3488 if self.mq.applied and self.mq.checkapplied and not force:
3481 parents = self.dirstate.parents()
3489 parents = self.dirstate.parents()
3482 patches = [s.node for s in self.mq.applied]
3490 patches = [s.node for s in self.mq.applied]
3483 if parents[0] in patches or parents[1] in patches:
3491 if parents[0] in patches or parents[1] in patches:
3484 raise error.Abort(errmsg)
3492 raise error.Abort(errmsg)
3485
3493
3486 def commit(self, text="", user=None, date=None, match=None,
3494 def commit(self, text="", user=None, date=None, match=None,
3487 force=False, editor=False, extra=None):
3495 force=False, editor=False, extra=None):
3488 if extra is None:
3496 if extra is None:
3489 extra = {}
3497 extra = {}
3490 self.abortifwdirpatched(
3498 self.abortifwdirpatched(
3491 _('cannot commit over an applied mq patch'),
3499 _('cannot commit over an applied mq patch'),
3492 force)
3500 force)
3493
3501
3494 return super(mqrepo, self).commit(text, user, date, match, force,
3502 return super(mqrepo, self).commit(text, user, date, match, force,
3495 editor, extra)
3503 editor, extra)
3496
3504
3497 def checkpush(self, pushop):
3505 def checkpush(self, pushop):
3498 if self.mq.applied and self.mq.checkapplied and not pushop.force:
3506 if self.mq.applied and self.mq.checkapplied and not pushop.force:
3499 outapplied = [e.node for e in self.mq.applied]
3507 outapplied = [e.node for e in self.mq.applied]
3500 if pushop.revs:
3508 if pushop.revs:
3501 # Assume applied patches have no non-patch descendants and
3509 # Assume applied patches have no non-patch descendants and
3502 # are not on remote already. Filtering any changeset not
3510 # are not on remote already. Filtering any changeset not
3503 # pushed.
3511 # pushed.
3504 heads = set(pushop.revs)
3512 heads = set(pushop.revs)
3505 for node in reversed(outapplied):
3513 for node in reversed(outapplied):
3506 if node in heads:
3514 if node in heads:
3507 break
3515 break
3508 else:
3516 else:
3509 outapplied.pop()
3517 outapplied.pop()
3510 # looking for pushed and shared changeset
3518 # looking for pushed and shared changeset
3511 for node in outapplied:
3519 for node in outapplied:
3512 if self[node].phase() < phases.secret:
3520 if self[node].phase() < phases.secret:
3513 raise error.Abort(_('source has mq patches applied'))
3521 raise error.Abort(_('source has mq patches applied'))
3514 # no non-secret patches pushed
3522 # no non-secret patches pushed
3515 super(mqrepo, self).checkpush(pushop)
3523 super(mqrepo, self).checkpush(pushop)
3516
3524
3517 def _findtags(self):
3525 def _findtags(self):
3518 '''augment tags from base class with patch tags'''
3526 '''augment tags from base class with patch tags'''
3519 result = super(mqrepo, self)._findtags()
3527 result = super(mqrepo, self)._findtags()
3520
3528
3521 q = self.mq
3529 q = self.mq
3522 if not q.applied:
3530 if not q.applied:
3523 return result
3531 return result
3524
3532
3525 mqtags = [(patch.node, patch.name) for patch in q.applied]
3533 mqtags = [(patch.node, patch.name) for patch in q.applied]
3526
3534
3527 try:
3535 try:
3528 # for now ignore filtering business
3536 # for now ignore filtering business
3529 self.unfiltered().changelog.rev(mqtags[-1][0])
3537 self.unfiltered().changelog.rev(mqtags[-1][0])
3530 except error.LookupError:
3538 except error.LookupError:
3531 self.ui.warn(_('mq status file refers to unknown node %s\n')
3539 self.ui.warn(_('mq status file refers to unknown node %s\n')
3532 % short(mqtags[-1][0]))
3540 % short(mqtags[-1][0]))
3533 return result
3541 return result
3534
3542
3535 # do not add fake tags for filtered revisions
3543 # do not add fake tags for filtered revisions
3536 included = self.changelog.hasnode
3544 included = self.changelog.hasnode
3537 mqtags = [mqt for mqt in mqtags if included(mqt[0])]
3545 mqtags = [mqt for mqt in mqtags if included(mqt[0])]
3538 if not mqtags:
3546 if not mqtags:
3539 return result
3547 return result
3540
3548
3541 mqtags.append((mqtags[-1][0], 'qtip'))
3549 mqtags.append((mqtags[-1][0], 'qtip'))
3542 mqtags.append((mqtags[0][0], 'qbase'))
3550 mqtags.append((mqtags[0][0], 'qbase'))
3543 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3551 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3544 tags = result[0]
3552 tags = result[0]
3545 for patch in mqtags:
3553 for patch in mqtags:
3546 if patch[1] in tags:
3554 if patch[1] in tags:
3547 self.ui.warn(_('tag %s overrides mq patch of the same '
3555 self.ui.warn(_('tag %s overrides mq patch of the same '
3548 'name\n') % patch[1])
3556 'name\n') % patch[1])
3549 else:
3557 else:
3550 tags[patch[1]] = patch[0]
3558 tags[patch[1]] = patch[0]
3551
3559
3552 return result
3560 return result
3553
3561
3554 if repo.local():
3562 if repo.local():
3555 repo.__class__ = mqrepo
3563 repo.__class__ = mqrepo
3556
3564
3557 repo._phasedefaults.append(mqphasedefaults)
3565 repo._phasedefaults.append(mqphasedefaults)
3558
3566
3559 def mqimport(orig, ui, repo, *args, **kwargs):
3567 def mqimport(orig, ui, repo, *args, **kwargs):
3560 if (util.safehasattr(repo, 'abortifwdirpatched')
3568 if (util.safehasattr(repo, 'abortifwdirpatched')
3561 and not kwargs.get(r'no_commit', False)):
3569 and not kwargs.get(r'no_commit', False)):
3562 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3570 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3563 kwargs.get(r'force'))
3571 kwargs.get(r'force'))
3564 return orig(ui, repo, *args, **kwargs)
3572 return orig(ui, repo, *args, **kwargs)
3565
3573
3566 def mqinit(orig, ui, *args, **kwargs):
3574 def mqinit(orig, ui, *args, **kwargs):
3567 mq = kwargs.pop(r'mq', None)
3575 mq = kwargs.pop(r'mq', None)
3568
3576
3569 if not mq:
3577 if not mq:
3570 return orig(ui, *args, **kwargs)
3578 return orig(ui, *args, **kwargs)
3571
3579
3572 if args:
3580 if args:
3573 repopath = args[0]
3581 repopath = args[0]
3574 if not hg.islocal(repopath):
3582 if not hg.islocal(repopath):
3575 raise error.Abort(_('only a local queue repository '
3583 raise error.Abort(_('only a local queue repository '
3576 'may be initialized'))
3584 'may be initialized'))
3577 else:
3585 else:
3578 repopath = cmdutil.findrepo(pycompat.getcwd())
3586 repopath = cmdutil.findrepo(pycompat.getcwd())
3579 if not repopath:
3587 if not repopath:
3580 raise error.Abort(_('there is no Mercurial repository here '
3588 raise error.Abort(_('there is no Mercurial repository here '
3581 '(.hg not found)'))
3589 '(.hg not found)'))
3582 repo = hg.repository(ui, repopath)
3590 repo = hg.repository(ui, repopath)
3583 return qinit(ui, repo, True)
3591 return qinit(ui, repo, True)
3584
3592
3585 def mqcommand(orig, ui, repo, *args, **kwargs):
3593 def mqcommand(orig, ui, repo, *args, **kwargs):
3586 """Add --mq option to operate on patch repository instead of main"""
3594 """Add --mq option to operate on patch repository instead of main"""
3587
3595
3588 # some commands do not like getting unknown options
3596 # some commands do not like getting unknown options
3589 mq = kwargs.pop(r'mq', None)
3597 mq = kwargs.pop(r'mq', None)
3590
3598
3591 if not mq:
3599 if not mq:
3592 return orig(ui, repo, *args, **kwargs)
3600 return orig(ui, repo, *args, **kwargs)
3593
3601
3594 q = repo.mq
3602 q = repo.mq
3595 r = q.qrepo()
3603 r = q.qrepo()
3596 if not r:
3604 if not r:
3597 raise error.Abort(_('no queue repository'))
3605 raise error.Abort(_('no queue repository'))
3598 return orig(r.ui, r, *args, **kwargs)
3606 return orig(r.ui, r, *args, **kwargs)
3599
3607
3600 def summaryhook(ui, repo):
3608 def summaryhook(ui, repo):
3601 q = repo.mq
3609 q = repo.mq
3602 m = []
3610 m = []
3603 a, u = len(q.applied), len(q.unapplied(repo))
3611 a, u = len(q.applied), len(q.unapplied(repo))
3604 if a:
3612 if a:
3605 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3613 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3606 if u:
3614 if u:
3607 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3615 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3608 if m:
3616 if m:
3609 # i18n: column positioning for "hg summary"
3617 # i18n: column positioning for "hg summary"
3610 ui.write(_("mq: %s\n") % ', '.join(m))
3618 ui.write(_("mq: %s\n") % ', '.join(m))
3611 else:
3619 else:
3612 # i18n: column positioning for "hg summary"
3620 # i18n: column positioning for "hg summary"
3613 ui.note(_("mq: (empty queue)\n"))
3621 ui.note(_("mq: (empty queue)\n"))
3614
3622
3615 revsetpredicate = registrar.revsetpredicate()
3623 revsetpredicate = registrar.revsetpredicate()
3616
3624
3617 @revsetpredicate('mq()')
3625 @revsetpredicate('mq()')
3618 def revsetmq(repo, subset, x):
3626 def revsetmq(repo, subset, x):
3619 """Changesets managed by MQ.
3627 """Changesets managed by MQ.
3620 """
3628 """
3621 revsetlang.getargs(x, 0, 0, _("mq takes no arguments"))
3629 revsetlang.getargs(x, 0, 0, _("mq takes no arguments"))
3622 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3630 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])
3631 return smartset.baseset([r for r in subset if r in applied])
3624
3632
3625 # tell hggettext to extract docstrings from these functions:
3633 # tell hggettext to extract docstrings from these functions:
3626 i18nfunctions = [revsetmq]
3634 i18nfunctions = [revsetmq]
3627
3635
3628 def extsetup(ui):
3636 def extsetup(ui):
3629 # Ensure mq wrappers are called first, regardless of extension load order by
3637 # 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.
3638 # NOT wrapping in uisetup() and instead deferring to init stage two here.
3631 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3639 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3632
3640
3633 extensions.wrapcommand(commands.table, 'import', mqimport)
3641 extensions.wrapcommand(commands.table, 'import', mqimport)
3634 cmdutil.summaryhooks.add('mq', summaryhook)
3642 cmdutil.summaryhooks.add('mq', summaryhook)
3635
3643
3636 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3644 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3637 entry[1].extend(mqopt)
3645 entry[1].extend(mqopt)
3638
3646
3639 def dotable(cmdtable):
3647 def dotable(cmdtable):
3640 for cmd, entry in cmdtable.iteritems():
3648 for cmd, entry in cmdtable.iteritems():
3641 cmd = cmdutil.parsealiases(cmd)[0]
3649 cmd = cmdutil.parsealiases(cmd)[0]
3642 func = entry[0]
3650 func = entry[0]
3643 if func.norepo:
3651 if func.norepo:
3644 continue
3652 continue
3645 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3653 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3646 entry[1].extend(mqopt)
3654 entry[1].extend(mqopt)
3647
3655
3648 dotable(commands.table)
3656 dotable(commands.table)
3649
3657
3650 for extname, extmodule in extensions.extensions():
3658 for extname, extmodule in extensions.extensions():
3651 if extmodule.__file__ != __file__:
3659 if extmodule.__file__ != __file__:
3652 dotable(getattr(extmodule, 'cmdtable', {}))
3660 dotable(getattr(extmodule, 'cmdtable', {}))
3653
3661
3654 colortable = {'qguard.negative': 'red',
3662 colortable = {'qguard.negative': 'red',
3655 'qguard.positive': 'yellow',
3663 'qguard.positive': 'yellow',
3656 'qguard.unguarded': 'green',
3664 'qguard.unguarded': 'green',
3657 'qseries.applied': 'blue bold underline',
3665 'qseries.applied': 'blue bold underline',
3658 'qseries.guarded': 'black bold',
3666 'qseries.guarded': 'black bold',
3659 'qseries.missing': 'red bold',
3667 'qseries.missing': 'red bold',
3660 'qseries.unapplied': 'black bold'}
3668 'qseries.unapplied': 'black bold'}
General Comments 0
You need to be logged in to leave comments. Login now