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