##// END OF EJS Templates
py3: use pycompat.bytestr() where repr in involved...
Pulkit Goyal -
r37577:c4a0626f default
parent child Browse files
Show More
@@ -1,3668 +1,3668 b''
1 # mq.py - patch queues for mercurial
1 # mq.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''manage a stack of patches
8 '''manage a stack of patches
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use :hg:`help COMMAND` for more details)::
17 Common tasks (use :hg:`help COMMAND` for more details)::
18
18
19 create new patch qnew
19 create new patch qnew
20 import existing patch qimport
20 import existing patch qimport
21
21
22 print patch series qseries
22 print patch series qseries
23 print applied patches qapplied
23 print applied patches qapplied
24
24
25 add known patch to applied stack qpush
25 add known patch to applied stack qpush
26 remove patch from applied stack qpop
26 remove patch from applied stack qpop
27 refresh contents of top applied patch qrefresh
27 refresh contents of top applied patch qrefresh
28
28
29 By default, mq will automatically use git patches when required to
29 By default, mq will automatically use git patches when required to
30 avoid losing file mode changes, copy records, binary files or empty
30 avoid losing file mode changes, copy records, binary files or empty
31 files creations or deletions. This behavior can be configured with::
31 files creations or deletions. This behavior can be configured with::
32
32
33 [mq]
33 [mq]
34 git = auto/keep/yes/no
34 git = auto/keep/yes/no
35
35
36 If set to 'keep', mq will obey the [diff] section configuration while
36 If set to 'keep', mq will obey the [diff] section configuration while
37 preserving existing git patches upon qrefresh. If set to 'yes' or
37 preserving existing git patches upon qrefresh. If set to 'yes' or
38 'no', mq will override the [diff] section and always generate git or
38 'no', mq will override the [diff] section and always generate git or
39 regular patches, possibly losing data in the second case.
39 regular patches, possibly losing data in the second case.
40
40
41 It may be desirable for mq changesets to be kept in the secret phase (see
41 It may be desirable for mq changesets to be kept in the secret phase (see
42 :hg:`help phases`), which can be enabled with the following setting::
42 :hg:`help phases`), which can be enabled with the following setting::
43
43
44 [mq]
44 [mq]
45 secret = True
45 secret = True
46
46
47 You will by default be managing a patch queue named "patches". You can
47 You will by default be managing a patch queue named "patches". You can
48 create other, independent patch queues with the :hg:`qqueue` command.
48 create other, independent patch queues with the :hg:`qqueue` command.
49
49
50 If the working directory contains uncommitted files, qpush, qpop and
50 If the working directory contains uncommitted files, qpush, qpop and
51 qgoto abort immediately. If -f/--force is used, the changes are
51 qgoto abort immediately. If -f/--force is used, the changes are
52 discarded. Setting::
52 discarded. Setting::
53
53
54 [mq]
54 [mq]
55 keepchanges = True
55 keepchanges = True
56
56
57 make them behave as if --keep-changes were passed, and non-conflicting
57 make them behave as if --keep-changes were passed, and non-conflicting
58 local changes will be tolerated and preserved. If incompatible options
58 local changes will be tolerated and preserved. If incompatible options
59 such as -f/--force or --exact are passed, this setting is ignored.
59 such as -f/--force or --exact are passed, this setting is ignored.
60
60
61 This extension used to provide a strip command. This command now lives
61 This extension used to provide a strip command. This command now lives
62 in the strip extension.
62 in the strip extension.
63 '''
63 '''
64
64
65 from __future__ import absolute_import, print_function
65 from __future__ import absolute_import, print_function
66
66
67 import errno
67 import errno
68 import os
68 import os
69 import re
69 import re
70 import shutil
70 import shutil
71 from mercurial.i18n import _
71 from mercurial.i18n import _
72 from mercurial.node import (
72 from mercurial.node import (
73 bin,
73 bin,
74 hex,
74 hex,
75 nullid,
75 nullid,
76 nullrev,
76 nullrev,
77 short,
77 short,
78 )
78 )
79 from mercurial import (
79 from mercurial import (
80 cmdutil,
80 cmdutil,
81 commands,
81 commands,
82 dirstateguard,
82 dirstateguard,
83 encoding,
83 encoding,
84 error,
84 error,
85 extensions,
85 extensions,
86 hg,
86 hg,
87 localrepo,
87 localrepo,
88 lock as lockmod,
88 lock as lockmod,
89 logcmdutil,
89 logcmdutil,
90 patch as patchmod,
90 patch as patchmod,
91 phases,
91 phases,
92 pycompat,
92 pycompat,
93 registrar,
93 registrar,
94 revsetlang,
94 revsetlang,
95 scmutil,
95 scmutil,
96 smartset,
96 smartset,
97 subrepoutil,
97 subrepoutil,
98 util,
98 util,
99 vfs as vfsmod,
99 vfs as vfsmod,
100 )
100 )
101 from mercurial.utils import (
101 from mercurial.utils import (
102 dateutil,
102 dateutil,
103 stringutil,
103 stringutil,
104 )
104 )
105
105
106 release = lockmod.release
106 release = lockmod.release
107 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
107 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
108
108
109 cmdtable = {}
109 cmdtable = {}
110 command = registrar.command(cmdtable)
110 command = registrar.command(cmdtable)
111 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
111 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
112 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
112 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
113 # be specifying the version(s) of Mercurial they are tested with, or
113 # be specifying the version(s) of Mercurial they are tested with, or
114 # leave the attribute unspecified.
114 # leave the attribute unspecified.
115 testedwith = 'ships-with-hg-core'
115 testedwith = 'ships-with-hg-core'
116
116
117 configtable = {}
117 configtable = {}
118 configitem = registrar.configitem(configtable)
118 configitem = registrar.configitem(configtable)
119
119
120 configitem('mq', 'git',
120 configitem('mq', 'git',
121 default='auto',
121 default='auto',
122 )
122 )
123 configitem('mq', 'keepchanges',
123 configitem('mq', 'keepchanges',
124 default=False,
124 default=False,
125 )
125 )
126 configitem('mq', 'plain',
126 configitem('mq', 'plain',
127 default=False,
127 default=False,
128 )
128 )
129 configitem('mq', 'secret',
129 configitem('mq', 'secret',
130 default=False,
130 default=False,
131 )
131 )
132
132
133 # force load strip extension formerly included in mq and import some utility
133 # force load strip extension formerly included in mq and import some utility
134 try:
134 try:
135 stripext = extensions.find('strip')
135 stripext = extensions.find('strip')
136 except KeyError:
136 except KeyError:
137 # note: load is lazy so we could avoid the try-except,
137 # note: load is lazy so we could avoid the try-except,
138 # but I (marmoute) prefer this explicit code.
138 # but I (marmoute) prefer this explicit code.
139 class dummyui(object):
139 class dummyui(object):
140 def debug(self, msg):
140 def debug(self, msg):
141 pass
141 pass
142 stripext = extensions.load(dummyui(), 'strip', '')
142 stripext = extensions.load(dummyui(), 'strip', '')
143
143
144 strip = stripext.strip
144 strip = stripext.strip
145 checksubstate = stripext.checksubstate
145 checksubstate = stripext.checksubstate
146 checklocalchanges = stripext.checklocalchanges
146 checklocalchanges = stripext.checklocalchanges
147
147
148
148
149 # Patch names looks like unix-file names.
149 # Patch names looks like unix-file names.
150 # They must be joinable with queue directory and result in the patch path.
150 # They must be joinable with queue directory and result in the patch path.
151 normname = util.normpath
151 normname = util.normpath
152
152
153 class statusentry(object):
153 class statusentry(object):
154 def __init__(self, node, name):
154 def __init__(self, node, name):
155 self.node, self.name = node, name
155 self.node, self.name = node, name
156
156
157 def __bytes__(self):
157 def __bytes__(self):
158 return hex(self.node) + ':' + self.name
158 return hex(self.node) + ':' + self.name
159
159
160 __str__ = encoding.strmethod(__bytes__)
160 __str__ = encoding.strmethod(__bytes__)
161 __repr__ = encoding.strmethod(__bytes__)
161 __repr__ = encoding.strmethod(__bytes__)
162
162
163 # The order of the headers in 'hg export' HG patches:
163 # The order of the headers in 'hg export' HG patches:
164 HGHEADERS = [
164 HGHEADERS = [
165 # '# HG changeset patch',
165 # '# HG changeset patch',
166 '# User ',
166 '# User ',
167 '# Date ',
167 '# Date ',
168 '# ',
168 '# ',
169 '# Branch ',
169 '# Branch ',
170 '# Node ID ',
170 '# Node ID ',
171 '# Parent ', # can occur twice for merges - but that is not relevant for mq
171 '# Parent ', # can occur twice for merges - but that is not relevant for mq
172 ]
172 ]
173 # The order of headers in plain 'mail style' patches:
173 # The order of headers in plain 'mail style' patches:
174 PLAINHEADERS = {
174 PLAINHEADERS = {
175 'from': 0,
175 'from': 0,
176 'date': 1,
176 'date': 1,
177 'subject': 2,
177 'subject': 2,
178 }
178 }
179
179
180 def inserthgheader(lines, header, value):
180 def inserthgheader(lines, header, value):
181 """Assuming lines contains a HG patch header, add a header line with value.
181 """Assuming lines contains a HG patch header, add a header line with value.
182 >>> try: inserthgheader([], b'# Date ', b'z')
182 >>> try: inserthgheader([], b'# Date ', b'z')
183 ... except ValueError as inst: print("oops")
183 ... except ValueError as inst: print("oops")
184 oops
184 oops
185 >>> inserthgheader([b'# HG changeset patch'], b'# Date ', b'z')
185 >>> inserthgheader([b'# HG changeset patch'], b'# Date ', b'z')
186 ['# HG changeset patch', '# Date z']
186 ['# HG changeset patch', '# Date z']
187 >>> inserthgheader([b'# HG changeset patch', b''], b'# Date ', b'z')
187 >>> inserthgheader([b'# HG changeset patch', b''], b'# Date ', b'z')
188 ['# HG changeset patch', '# Date z', '']
188 ['# HG changeset patch', '# Date z', '']
189 >>> inserthgheader([b'# HG changeset patch', b'# User y'], b'# Date ', b'z')
189 >>> inserthgheader([b'# HG changeset patch', b'# User y'], b'# Date ', b'z')
190 ['# HG changeset patch', '# User y', '# Date z']
190 ['# HG changeset patch', '# User y', '# Date z']
191 >>> inserthgheader([b'# HG changeset patch', b'# Date x', b'# User y'],
191 >>> inserthgheader([b'# HG changeset patch', b'# Date x', b'# User y'],
192 ... b'# User ', b'z')
192 ... b'# User ', b'z')
193 ['# HG changeset patch', '# Date x', '# User z']
193 ['# HG changeset patch', '# Date x', '# User z']
194 >>> inserthgheader([b'# HG changeset patch', b'# Date y'], b'# Date ', b'z')
194 >>> inserthgheader([b'# HG changeset patch', b'# Date y'], b'# Date ', b'z')
195 ['# HG changeset patch', '# Date z']
195 ['# HG changeset patch', '# Date z']
196 >>> inserthgheader([b'# HG changeset patch', b'', b'# Date y'],
196 >>> inserthgheader([b'# HG changeset patch', b'', b'# Date y'],
197 ... b'# Date ', b'z')
197 ... b'# Date ', b'z')
198 ['# HG changeset patch', '# Date z', '', '# Date y']
198 ['# HG changeset patch', '# Date z', '', '# Date y']
199 >>> inserthgheader([b'# HG changeset patch', b'# Parent y'],
199 >>> inserthgheader([b'# HG changeset patch', b'# Parent y'],
200 ... b'# Date ', b'z')
200 ... b'# Date ', b'z')
201 ['# HG changeset patch', '# Date z', '# Parent y']
201 ['# HG changeset patch', '# Date z', '# Parent y']
202 """
202 """
203 start = lines.index('# HG changeset patch') + 1
203 start = lines.index('# HG changeset patch') + 1
204 newindex = HGHEADERS.index(header)
204 newindex = HGHEADERS.index(header)
205 bestpos = len(lines)
205 bestpos = len(lines)
206 for i in range(start, len(lines)):
206 for i in range(start, len(lines)):
207 line = lines[i]
207 line = lines[i]
208 if not line.startswith('# '):
208 if not line.startswith('# '):
209 bestpos = min(bestpos, i)
209 bestpos = min(bestpos, i)
210 break
210 break
211 for lineindex, h in enumerate(HGHEADERS):
211 for lineindex, h in enumerate(HGHEADERS):
212 if line.startswith(h):
212 if line.startswith(h):
213 if lineindex == newindex:
213 if lineindex == newindex:
214 lines[i] = header + value
214 lines[i] = header + value
215 return lines
215 return lines
216 if lineindex > newindex:
216 if lineindex > newindex:
217 bestpos = min(bestpos, i)
217 bestpos = min(bestpos, i)
218 break # next line
218 break # next line
219 lines.insert(bestpos, header + value)
219 lines.insert(bestpos, header + value)
220 return lines
220 return lines
221
221
222 def insertplainheader(lines, header, value):
222 def insertplainheader(lines, header, value):
223 """For lines containing a plain patch header, add a header line with value.
223 """For lines containing a plain patch header, add a header line with value.
224 >>> insertplainheader([], b'Date', b'z')
224 >>> insertplainheader([], b'Date', b'z')
225 ['Date: z']
225 ['Date: z']
226 >>> insertplainheader([b''], b'Date', b'z')
226 >>> insertplainheader([b''], b'Date', b'z')
227 ['Date: z', '']
227 ['Date: z', '']
228 >>> insertplainheader([b'x'], b'Date', b'z')
228 >>> insertplainheader([b'x'], b'Date', b'z')
229 ['Date: z', '', 'x']
229 ['Date: z', '', 'x']
230 >>> insertplainheader([b'From: y', b'x'], b'Date', b'z')
230 >>> insertplainheader([b'From: y', b'x'], b'Date', b'z')
231 ['From: y', 'Date: z', '', 'x']
231 ['From: y', 'Date: z', '', 'x']
232 >>> insertplainheader([b' date : x', b' from : y', b''], b'From', b'z')
232 >>> insertplainheader([b' date : x', b' from : y', b''], b'From', b'z')
233 [' date : x', 'From: z', '']
233 [' date : x', 'From: z', '']
234 >>> insertplainheader([b'', b'Date: y'], b'Date', b'z')
234 >>> insertplainheader([b'', b'Date: y'], b'Date', b'z')
235 ['Date: z', '', 'Date: y']
235 ['Date: z', '', 'Date: y']
236 >>> insertplainheader([b'foo: bar', b'DATE: z', b'x'], b'From', b'y')
236 >>> insertplainheader([b'foo: bar', b'DATE: z', b'x'], b'From', b'y')
237 ['From: y', 'foo: bar', 'DATE: z', '', 'x']
237 ['From: y', 'foo: bar', 'DATE: z', '', 'x']
238 """
238 """
239 newprio = PLAINHEADERS[header.lower()]
239 newprio = PLAINHEADERS[header.lower()]
240 bestpos = len(lines)
240 bestpos = len(lines)
241 for i, line in enumerate(lines):
241 for i, line in enumerate(lines):
242 if ':' in line:
242 if ':' in line:
243 lheader = line.split(':', 1)[0].strip().lower()
243 lheader = line.split(':', 1)[0].strip().lower()
244 lprio = PLAINHEADERS.get(lheader, newprio + 1)
244 lprio = PLAINHEADERS.get(lheader, newprio + 1)
245 if lprio == newprio:
245 if lprio == newprio:
246 lines[i] = '%s: %s' % (header, value)
246 lines[i] = '%s: %s' % (header, value)
247 return lines
247 return lines
248 if lprio > newprio and i < bestpos:
248 if lprio > newprio and i < bestpos:
249 bestpos = i
249 bestpos = i
250 else:
250 else:
251 if line:
251 if line:
252 lines.insert(i, '')
252 lines.insert(i, '')
253 if i < bestpos:
253 if i < bestpos:
254 bestpos = i
254 bestpos = i
255 break
255 break
256 lines.insert(bestpos, '%s: %s' % (header, value))
256 lines.insert(bestpos, '%s: %s' % (header, value))
257 return lines
257 return lines
258
258
259 class patchheader(object):
259 class patchheader(object):
260 def __init__(self, pf, plainmode=False):
260 def __init__(self, pf, plainmode=False):
261 def eatdiff(lines):
261 def eatdiff(lines):
262 while lines:
262 while lines:
263 l = lines[-1]
263 l = lines[-1]
264 if (l.startswith("diff -") or
264 if (l.startswith("diff -") or
265 l.startswith("Index:") or
265 l.startswith("Index:") or
266 l.startswith("===========")):
266 l.startswith("===========")):
267 del lines[-1]
267 del lines[-1]
268 else:
268 else:
269 break
269 break
270 def eatempty(lines):
270 def eatempty(lines):
271 while lines:
271 while lines:
272 if not lines[-1].strip():
272 if not lines[-1].strip():
273 del lines[-1]
273 del lines[-1]
274 else:
274 else:
275 break
275 break
276
276
277 message = []
277 message = []
278 comments = []
278 comments = []
279 user = None
279 user = None
280 date = None
280 date = None
281 parent = None
281 parent = None
282 format = None
282 format = None
283 subject = None
283 subject = None
284 branch = None
284 branch = None
285 nodeid = None
285 nodeid = None
286 diffstart = 0
286 diffstart = 0
287
287
288 for line in open(pf, 'rb'):
288 for line in open(pf, 'rb'):
289 line = line.rstrip()
289 line = line.rstrip()
290 if (line.startswith('diff --git')
290 if (line.startswith('diff --git')
291 or (diffstart and line.startswith('+++ '))):
291 or (diffstart and line.startswith('+++ '))):
292 diffstart = 2
292 diffstart = 2
293 break
293 break
294 diffstart = 0 # reset
294 diffstart = 0 # reset
295 if line.startswith("--- "):
295 if line.startswith("--- "):
296 diffstart = 1
296 diffstart = 1
297 continue
297 continue
298 elif format == "hgpatch":
298 elif format == "hgpatch":
299 # parse values when importing the result of an hg export
299 # parse values when importing the result of an hg export
300 if line.startswith("# User "):
300 if line.startswith("# User "):
301 user = line[7:]
301 user = line[7:]
302 elif line.startswith("# Date "):
302 elif line.startswith("# Date "):
303 date = line[7:]
303 date = line[7:]
304 elif line.startswith("# Parent "):
304 elif line.startswith("# Parent "):
305 parent = line[9:].lstrip() # handle double trailing space
305 parent = line[9:].lstrip() # handle double trailing space
306 elif line.startswith("# Branch "):
306 elif line.startswith("# Branch "):
307 branch = line[9:]
307 branch = line[9:]
308 elif line.startswith("# Node ID "):
308 elif line.startswith("# Node ID "):
309 nodeid = line[10:]
309 nodeid = line[10:]
310 elif not line.startswith("# ") and line:
310 elif not line.startswith("# ") and line:
311 message.append(line)
311 message.append(line)
312 format = None
312 format = None
313 elif line == '# HG changeset patch':
313 elif line == '# HG changeset patch':
314 message = []
314 message = []
315 format = "hgpatch"
315 format = "hgpatch"
316 elif (format != "tagdone" and (line.startswith("Subject: ") or
316 elif (format != "tagdone" and (line.startswith("Subject: ") or
317 line.startswith("subject: "))):
317 line.startswith("subject: "))):
318 subject = line[9:]
318 subject = line[9:]
319 format = "tag"
319 format = "tag"
320 elif (format != "tagdone" and (line.startswith("From: ") or
320 elif (format != "tagdone" and (line.startswith("From: ") or
321 line.startswith("from: "))):
321 line.startswith("from: "))):
322 user = line[6:]
322 user = line[6:]
323 format = "tag"
323 format = "tag"
324 elif (format != "tagdone" and (line.startswith("Date: ") or
324 elif (format != "tagdone" and (line.startswith("Date: ") or
325 line.startswith("date: "))):
325 line.startswith("date: "))):
326 date = line[6:]
326 date = line[6:]
327 format = "tag"
327 format = "tag"
328 elif format == "tag" and line == "":
328 elif format == "tag" and line == "":
329 # when looking for tags (subject: from: etc) they
329 # when looking for tags (subject: from: etc) they
330 # end once you find a blank line in the source
330 # end once you find a blank line in the source
331 format = "tagdone"
331 format = "tagdone"
332 elif message or line:
332 elif message or line:
333 message.append(line)
333 message.append(line)
334 comments.append(line)
334 comments.append(line)
335
335
336 eatdiff(message)
336 eatdiff(message)
337 eatdiff(comments)
337 eatdiff(comments)
338 # Remember the exact starting line of the patch diffs before consuming
338 # Remember the exact starting line of the patch diffs before consuming
339 # empty lines, for external use by TortoiseHg and others
339 # empty lines, for external use by TortoiseHg and others
340 self.diffstartline = len(comments)
340 self.diffstartline = len(comments)
341 eatempty(message)
341 eatempty(message)
342 eatempty(comments)
342 eatempty(comments)
343
343
344 # make sure message isn't empty
344 # make sure message isn't empty
345 if format and format.startswith("tag") and subject:
345 if format and format.startswith("tag") and subject:
346 message.insert(0, subject)
346 message.insert(0, subject)
347
347
348 self.message = message
348 self.message = message
349 self.comments = comments
349 self.comments = comments
350 self.user = user
350 self.user = user
351 self.date = date
351 self.date = date
352 self.parent = parent
352 self.parent = parent
353 # nodeid and branch are for external use by TortoiseHg and others
353 # nodeid and branch are for external use by TortoiseHg and others
354 self.nodeid = nodeid
354 self.nodeid = nodeid
355 self.branch = branch
355 self.branch = branch
356 self.haspatch = diffstart > 1
356 self.haspatch = diffstart > 1
357 self.plainmode = (plainmode or
357 self.plainmode = (plainmode or
358 '# HG changeset patch' not in self.comments and
358 '# HG changeset patch' not in self.comments and
359 any(c.startswith('Date: ') or
359 any(c.startswith('Date: ') or
360 c.startswith('From: ')
360 c.startswith('From: ')
361 for c in self.comments))
361 for c in self.comments))
362
362
363 def setuser(self, user):
363 def setuser(self, user):
364 try:
364 try:
365 inserthgheader(self.comments, '# User ', user)
365 inserthgheader(self.comments, '# User ', user)
366 except ValueError:
366 except ValueError:
367 if self.plainmode:
367 if self.plainmode:
368 insertplainheader(self.comments, 'From', user)
368 insertplainheader(self.comments, 'From', user)
369 else:
369 else:
370 tmp = ['# HG changeset patch', '# User ' + user]
370 tmp = ['# HG changeset patch', '# User ' + user]
371 self.comments = tmp + self.comments
371 self.comments = tmp + self.comments
372 self.user = user
372 self.user = user
373
373
374 def setdate(self, date):
374 def setdate(self, date):
375 try:
375 try:
376 inserthgheader(self.comments, '# Date ', date)
376 inserthgheader(self.comments, '# Date ', date)
377 except ValueError:
377 except ValueError:
378 if self.plainmode:
378 if self.plainmode:
379 insertplainheader(self.comments, 'Date', date)
379 insertplainheader(self.comments, 'Date', date)
380 else:
380 else:
381 tmp = ['# HG changeset patch', '# Date ' + date]
381 tmp = ['# HG changeset patch', '# Date ' + date]
382 self.comments = tmp + self.comments
382 self.comments = tmp + self.comments
383 self.date = date
383 self.date = date
384
384
385 def setparent(self, parent):
385 def setparent(self, parent):
386 try:
386 try:
387 inserthgheader(self.comments, '# Parent ', parent)
387 inserthgheader(self.comments, '# Parent ', parent)
388 except ValueError:
388 except ValueError:
389 if not self.plainmode:
389 if not self.plainmode:
390 tmp = ['# HG changeset patch', '# Parent ' + parent]
390 tmp = ['# HG changeset patch', '# Parent ' + parent]
391 self.comments = tmp + self.comments
391 self.comments = tmp + self.comments
392 self.parent = parent
392 self.parent = parent
393
393
394 def setmessage(self, message):
394 def setmessage(self, message):
395 if self.comments:
395 if self.comments:
396 self._delmsg()
396 self._delmsg()
397 self.message = [message]
397 self.message = [message]
398 if message:
398 if message:
399 if self.plainmode and self.comments and self.comments[-1]:
399 if self.plainmode and self.comments and self.comments[-1]:
400 self.comments.append('')
400 self.comments.append('')
401 self.comments.append(message)
401 self.comments.append(message)
402
402
403 def __bytes__(self):
403 def __bytes__(self):
404 s = '\n'.join(self.comments).rstrip()
404 s = '\n'.join(self.comments).rstrip()
405 if not s:
405 if not s:
406 return ''
406 return ''
407 return s + '\n\n'
407 return s + '\n\n'
408
408
409 __str__ = encoding.strmethod(__bytes__)
409 __str__ = encoding.strmethod(__bytes__)
410
410
411 def _delmsg(self):
411 def _delmsg(self):
412 '''Remove existing message, keeping the rest of the comments fields.
412 '''Remove existing message, keeping the rest of the comments fields.
413 If comments contains 'subject: ', message will prepend
413 If comments contains 'subject: ', message will prepend
414 the field and a blank line.'''
414 the field and a blank line.'''
415 if self.message:
415 if self.message:
416 subj = 'subject: ' + self.message[0].lower()
416 subj = 'subject: ' + self.message[0].lower()
417 for i in xrange(len(self.comments)):
417 for i in xrange(len(self.comments)):
418 if subj == self.comments[i].lower():
418 if subj == self.comments[i].lower():
419 del self.comments[i]
419 del self.comments[i]
420 self.message = self.message[2:]
420 self.message = self.message[2:]
421 break
421 break
422 ci = 0
422 ci = 0
423 for mi in self.message:
423 for mi in self.message:
424 while mi != self.comments[ci]:
424 while mi != self.comments[ci]:
425 ci += 1
425 ci += 1
426 del self.comments[ci]
426 del self.comments[ci]
427
427
428 def newcommit(repo, phase, *args, **kwargs):
428 def newcommit(repo, phase, *args, **kwargs):
429 """helper dedicated to ensure a commit respect mq.secret setting
429 """helper dedicated to ensure a commit respect mq.secret setting
430
430
431 It should be used instead of repo.commit inside the mq source for operation
431 It should be used instead of repo.commit inside the mq source for operation
432 creating new changeset.
432 creating new changeset.
433 """
433 """
434 repo = repo.unfiltered()
434 repo = repo.unfiltered()
435 if phase is None:
435 if phase is None:
436 if repo.ui.configbool('mq', 'secret'):
436 if repo.ui.configbool('mq', 'secret'):
437 phase = phases.secret
437 phase = phases.secret
438 overrides = {('ui', 'allowemptycommit'): True}
438 overrides = {('ui', 'allowemptycommit'): True}
439 if phase is not None:
439 if phase is not None:
440 overrides[('phases', 'new-commit')] = phase
440 overrides[('phases', 'new-commit')] = phase
441 with repo.ui.configoverride(overrides, 'mq'):
441 with repo.ui.configoverride(overrides, 'mq'):
442 repo.ui.setconfig('ui', 'allowemptycommit', True)
442 repo.ui.setconfig('ui', 'allowemptycommit', True)
443 return repo.commit(*args, **kwargs)
443 return repo.commit(*args, **kwargs)
444
444
445 class AbortNoCleanup(error.Abort):
445 class AbortNoCleanup(error.Abort):
446 pass
446 pass
447
447
448 class queue(object):
448 class queue(object):
449 def __init__(self, ui, baseui, path, patchdir=None):
449 def __init__(self, ui, baseui, path, patchdir=None):
450 self.basepath = path
450 self.basepath = path
451 try:
451 try:
452 with open(os.path.join(path, 'patches.queue'), r'rb') as fh:
452 with open(os.path.join(path, 'patches.queue'), r'rb') as fh:
453 cur = fh.read().rstrip()
453 cur = fh.read().rstrip()
454
454
455 if not cur:
455 if not cur:
456 curpath = os.path.join(path, 'patches')
456 curpath = os.path.join(path, 'patches')
457 else:
457 else:
458 curpath = os.path.join(path, 'patches-' + cur)
458 curpath = os.path.join(path, 'patches-' + cur)
459 except IOError:
459 except IOError:
460 curpath = os.path.join(path, 'patches')
460 curpath = os.path.join(path, 'patches')
461 self.path = patchdir or curpath
461 self.path = patchdir or curpath
462 self.opener = vfsmod.vfs(self.path)
462 self.opener = vfsmod.vfs(self.path)
463 self.ui = ui
463 self.ui = ui
464 self.baseui = baseui
464 self.baseui = baseui
465 self.applieddirty = False
465 self.applieddirty = False
466 self.seriesdirty = False
466 self.seriesdirty = False
467 self.added = []
467 self.added = []
468 self.seriespath = "series"
468 self.seriespath = "series"
469 self.statuspath = "status"
469 self.statuspath = "status"
470 self.guardspath = "guards"
470 self.guardspath = "guards"
471 self.activeguards = None
471 self.activeguards = None
472 self.guardsdirty = False
472 self.guardsdirty = False
473 # Handle mq.git as a bool with extended values
473 # Handle mq.git as a bool with extended values
474 gitmode = ui.config('mq', 'git').lower()
474 gitmode = ui.config('mq', 'git').lower()
475 boolmode = stringutil.parsebool(gitmode)
475 boolmode = stringutil.parsebool(gitmode)
476 if boolmode is not None:
476 if boolmode is not None:
477 if boolmode:
477 if boolmode:
478 gitmode = 'yes'
478 gitmode = 'yes'
479 else:
479 else:
480 gitmode = 'no'
480 gitmode = 'no'
481 self.gitmode = gitmode
481 self.gitmode = gitmode
482 # deprecated config: mq.plain
482 # deprecated config: mq.plain
483 self.plainmode = ui.configbool('mq', 'plain')
483 self.plainmode = ui.configbool('mq', 'plain')
484 self.checkapplied = True
484 self.checkapplied = True
485
485
486 @util.propertycache
486 @util.propertycache
487 def applied(self):
487 def applied(self):
488 def parselines(lines):
488 def parselines(lines):
489 for l in lines:
489 for l in lines:
490 entry = l.split(':', 1)
490 entry = l.split(':', 1)
491 if len(entry) > 1:
491 if len(entry) > 1:
492 n, name = entry
492 n, name = entry
493 yield statusentry(bin(n), name)
493 yield statusentry(bin(n), name)
494 elif l.strip():
494 elif l.strip():
495 self.ui.warn(_('malformated mq status line: %s\n') % entry)
495 self.ui.warn(_('malformated mq status line: %s\n') % entry)
496 # else we ignore empty lines
496 # else we ignore empty lines
497 try:
497 try:
498 lines = self.opener.read(self.statuspath).splitlines()
498 lines = self.opener.read(self.statuspath).splitlines()
499 return list(parselines(lines))
499 return list(parselines(lines))
500 except IOError as e:
500 except IOError as e:
501 if e.errno == errno.ENOENT:
501 if e.errno == errno.ENOENT:
502 return []
502 return []
503 raise
503 raise
504
504
505 @util.propertycache
505 @util.propertycache
506 def fullseries(self):
506 def fullseries(self):
507 try:
507 try:
508 return self.opener.read(self.seriespath).splitlines()
508 return self.opener.read(self.seriespath).splitlines()
509 except IOError as e:
509 except IOError as e:
510 if e.errno == errno.ENOENT:
510 if e.errno == errno.ENOENT:
511 return []
511 return []
512 raise
512 raise
513
513
514 @util.propertycache
514 @util.propertycache
515 def series(self):
515 def series(self):
516 self.parseseries()
516 self.parseseries()
517 return self.series
517 return self.series
518
518
519 @util.propertycache
519 @util.propertycache
520 def seriesguards(self):
520 def seriesguards(self):
521 self.parseseries()
521 self.parseseries()
522 return self.seriesguards
522 return self.seriesguards
523
523
524 def invalidate(self):
524 def invalidate(self):
525 for a in 'applied fullseries series seriesguards'.split():
525 for a in 'applied fullseries series seriesguards'.split():
526 if a in self.__dict__:
526 if a in self.__dict__:
527 delattr(self, a)
527 delattr(self, a)
528 self.applieddirty = False
528 self.applieddirty = False
529 self.seriesdirty = False
529 self.seriesdirty = False
530 self.guardsdirty = False
530 self.guardsdirty = False
531 self.activeguards = None
531 self.activeguards = None
532
532
533 def diffopts(self, opts=None, patchfn=None, plain=False):
533 def diffopts(self, opts=None, patchfn=None, plain=False):
534 """Return diff options tweaked for this mq use, possibly upgrading to
534 """Return diff options tweaked for this mq use, possibly upgrading to
535 git format, and possibly plain and without lossy options."""
535 git format, and possibly plain and without lossy options."""
536 diffopts = patchmod.difffeatureopts(self.ui, opts,
536 diffopts = patchmod.difffeatureopts(self.ui, opts,
537 git=True, whitespace=not plain, formatchanging=not plain)
537 git=True, whitespace=not plain, formatchanging=not plain)
538 if self.gitmode == 'auto':
538 if self.gitmode == 'auto':
539 diffopts.upgrade = True
539 diffopts.upgrade = True
540 elif self.gitmode == 'keep':
540 elif self.gitmode == 'keep':
541 pass
541 pass
542 elif self.gitmode in ('yes', 'no'):
542 elif self.gitmode in ('yes', 'no'):
543 diffopts.git = self.gitmode == 'yes'
543 diffopts.git = self.gitmode == 'yes'
544 else:
544 else:
545 raise error.Abort(_('mq.git option can be auto/keep/yes/no'
545 raise error.Abort(_('mq.git option can be auto/keep/yes/no'
546 ' got %s') % self.gitmode)
546 ' got %s') % self.gitmode)
547 if patchfn:
547 if patchfn:
548 diffopts = self.patchopts(diffopts, patchfn)
548 diffopts = self.patchopts(diffopts, patchfn)
549 return diffopts
549 return diffopts
550
550
551 def patchopts(self, diffopts, *patches):
551 def patchopts(self, diffopts, *patches):
552 """Return a copy of input diff options with git set to true if
552 """Return a copy of input diff options with git set to true if
553 referenced patch is a git patch and should be preserved as such.
553 referenced patch is a git patch and should be preserved as such.
554 """
554 """
555 diffopts = diffopts.copy()
555 diffopts = diffopts.copy()
556 if not diffopts.git and self.gitmode == 'keep':
556 if not diffopts.git and self.gitmode == 'keep':
557 for patchfn in patches:
557 for patchfn in patches:
558 patchf = self.opener(patchfn, 'r')
558 patchf = self.opener(patchfn, 'r')
559 # if the patch was a git patch, refresh it as a git patch
559 # if the patch was a git patch, refresh it as a git patch
560 diffopts.git = any(line.startswith('diff --git')
560 diffopts.git = any(line.startswith('diff --git')
561 for line in patchf)
561 for line in patchf)
562 patchf.close()
562 patchf.close()
563 return diffopts
563 return diffopts
564
564
565 def join(self, *p):
565 def join(self, *p):
566 return os.path.join(self.path, *p)
566 return os.path.join(self.path, *p)
567
567
568 def findseries(self, patch):
568 def findseries(self, patch):
569 def matchpatch(l):
569 def matchpatch(l):
570 l = l.split('#', 1)[0]
570 l = l.split('#', 1)[0]
571 return l.strip() == patch
571 return l.strip() == patch
572 for index, l in enumerate(self.fullseries):
572 for index, l in enumerate(self.fullseries):
573 if matchpatch(l):
573 if matchpatch(l):
574 return index
574 return index
575 return None
575 return None
576
576
577 guard_re = re.compile(br'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
577 guard_re = re.compile(br'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
578
578
579 def parseseries(self):
579 def parseseries(self):
580 self.series = []
580 self.series = []
581 self.seriesguards = []
581 self.seriesguards = []
582 for l in self.fullseries:
582 for l in self.fullseries:
583 h = l.find('#')
583 h = l.find('#')
584 if h == -1:
584 if h == -1:
585 patch = l
585 patch = l
586 comment = ''
586 comment = ''
587 elif h == 0:
587 elif h == 0:
588 continue
588 continue
589 else:
589 else:
590 patch = l[:h]
590 patch = l[:h]
591 comment = l[h:]
591 comment = l[h:]
592 patch = patch.strip()
592 patch = patch.strip()
593 if patch:
593 if patch:
594 if patch in self.series:
594 if patch in self.series:
595 raise error.Abort(_('%s appears more than once in %s') %
595 raise error.Abort(_('%s appears more than once in %s') %
596 (patch, self.join(self.seriespath)))
596 (patch, self.join(self.seriespath)))
597 self.series.append(patch)
597 self.series.append(patch)
598 self.seriesguards.append(self.guard_re.findall(comment))
598 self.seriesguards.append(self.guard_re.findall(comment))
599
599
600 def checkguard(self, guard):
600 def checkguard(self, guard):
601 if not guard:
601 if not guard:
602 return _('guard cannot be an empty string')
602 return _('guard cannot be an empty string')
603 bad_chars = '# \t\r\n\f'
603 bad_chars = '# \t\r\n\f'
604 first = guard[0]
604 first = guard[0]
605 if first in '-+':
605 if first in '-+':
606 return (_('guard %r starts with invalid character: %r') %
606 return (_('guard %r starts with invalid character: %r') %
607 (guard, first))
607 (guard, first))
608 for c in bad_chars:
608 for c in bad_chars:
609 if c in guard:
609 if c in guard:
610 return _('invalid character in guard %r: %r') % (guard, c)
610 return _('invalid character in guard %r: %r') % (guard, c)
611
611
612 def setactive(self, guards):
612 def setactive(self, guards):
613 for guard in guards:
613 for guard in guards:
614 bad = self.checkguard(guard)
614 bad = self.checkguard(guard)
615 if bad:
615 if bad:
616 raise error.Abort(bad)
616 raise error.Abort(bad)
617 guards = sorted(set(guards))
617 guards = sorted(set(guards))
618 self.ui.debug('active guards: %s\n' % ' '.join(guards))
618 self.ui.debug('active guards: %s\n' % ' '.join(guards))
619 self.activeguards = guards
619 self.activeguards = guards
620 self.guardsdirty = True
620 self.guardsdirty = True
621
621
622 def active(self):
622 def active(self):
623 if self.activeguards is None:
623 if self.activeguards is None:
624 self.activeguards = []
624 self.activeguards = []
625 try:
625 try:
626 guards = self.opener.read(self.guardspath).split()
626 guards = self.opener.read(self.guardspath).split()
627 except IOError as err:
627 except IOError as err:
628 if err.errno != errno.ENOENT:
628 if err.errno != errno.ENOENT:
629 raise
629 raise
630 guards = []
630 guards = []
631 for i, guard in enumerate(guards):
631 for i, guard in enumerate(guards):
632 bad = self.checkguard(guard)
632 bad = self.checkguard(guard)
633 if bad:
633 if bad:
634 self.ui.warn('%s:%d: %s\n' %
634 self.ui.warn('%s:%d: %s\n' %
635 (self.join(self.guardspath), i + 1, bad))
635 (self.join(self.guardspath), i + 1, bad))
636 else:
636 else:
637 self.activeguards.append(guard)
637 self.activeguards.append(guard)
638 return self.activeguards
638 return self.activeguards
639
639
640 def setguards(self, idx, guards):
640 def setguards(self, idx, guards):
641 for g in guards:
641 for g in guards:
642 if len(g) < 2:
642 if len(g) < 2:
643 raise error.Abort(_('guard %r too short') % g)
643 raise error.Abort(_('guard %r too short') % g)
644 if g[0] not in '-+':
644 if g[0] not in '-+':
645 raise error.Abort(_('guard %r starts with invalid char') % g)
645 raise error.Abort(_('guard %r starts with invalid char') % g)
646 bad = self.checkguard(g[1:])
646 bad = self.checkguard(g[1:])
647 if bad:
647 if bad:
648 raise error.Abort(bad)
648 raise error.Abort(bad)
649 drop = self.guard_re.sub('', self.fullseries[idx])
649 drop = self.guard_re.sub('', self.fullseries[idx])
650 self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
650 self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
651 self.parseseries()
651 self.parseseries()
652 self.seriesdirty = True
652 self.seriesdirty = True
653
653
654 def pushable(self, idx):
654 def pushable(self, idx):
655 if isinstance(idx, bytes):
655 if isinstance(idx, bytes):
656 idx = self.series.index(idx)
656 idx = self.series.index(idx)
657 patchguards = self.seriesguards[idx]
657 patchguards = self.seriesguards[idx]
658 if not patchguards:
658 if not patchguards:
659 return True, None
659 return True, None
660 guards = self.active()
660 guards = self.active()
661 exactneg = [g for g in patchguards
661 exactneg = [g for g in patchguards
662 if g.startswith('-') and g[1:] in guards]
662 if g.startswith('-') and g[1:] in guards]
663 if exactneg:
663 if exactneg:
664 return False, pycompat.byterepr(exactneg[0])
664 return False, pycompat.byterepr(exactneg[0])
665 pos = [g for g in patchguards if g.startswith('+')]
665 pos = [g for g in patchguards if g.startswith('+')]
666 exactpos = [g for g in pos if g[1:] in guards]
666 exactpos = [g for g in pos if g[1:] in guards]
667 if pos:
667 if pos:
668 if exactpos:
668 if exactpos:
669 return True, pycompat.byterepr(exactpos[0])
669 return True, pycompat.byterepr(exactpos[0])
670 return False, ' '.join([pycompat.byterepr(p) for p in pos])
670 return False, ' '.join([pycompat.byterepr(p) for p in pos])
671 return True, ''
671 return True, ''
672
672
673 def explainpushable(self, idx, all_patches=False):
673 def explainpushable(self, idx, all_patches=False):
674 if all_patches:
674 if all_patches:
675 write = self.ui.write
675 write = self.ui.write
676 else:
676 else:
677 write = self.ui.warn
677 write = self.ui.warn
678
678
679 if all_patches or self.ui.verbose:
679 if all_patches or self.ui.verbose:
680 if isinstance(idx, bytes):
680 if isinstance(idx, bytes):
681 idx = self.series.index(idx)
681 idx = self.series.index(idx)
682 pushable, why = self.pushable(idx)
682 pushable, why = self.pushable(idx)
683 if all_patches and pushable:
683 if all_patches and pushable:
684 if why is None:
684 if why is None:
685 write(_('allowing %s - no guards in effect\n') %
685 write(_('allowing %s - no guards in effect\n') %
686 self.series[idx])
686 self.series[idx])
687 else:
687 else:
688 if not why:
688 if not why:
689 write(_('allowing %s - no matching negative guards\n') %
689 write(_('allowing %s - no matching negative guards\n') %
690 self.series[idx])
690 self.series[idx])
691 else:
691 else:
692 write(_('allowing %s - guarded by %s\n') %
692 write(_('allowing %s - guarded by %s\n') %
693 (self.series[idx], why))
693 (self.series[idx], why))
694 if not pushable:
694 if not pushable:
695 if why:
695 if why:
696 write(_('skipping %s - guarded by %s\n') %
696 write(_('skipping %s - guarded by %s\n') %
697 (self.series[idx], why))
697 (self.series[idx], why))
698 else:
698 else:
699 write(_('skipping %s - no matching guards\n') %
699 write(_('skipping %s - no matching guards\n') %
700 self.series[idx])
700 self.series[idx])
701
701
702 def savedirty(self):
702 def savedirty(self):
703 def writelist(items, path):
703 def writelist(items, path):
704 fp = self.opener(path, 'wb')
704 fp = self.opener(path, 'wb')
705 for i in items:
705 for i in items:
706 fp.write("%s\n" % i)
706 fp.write("%s\n" % i)
707 fp.close()
707 fp.close()
708 if self.applieddirty:
708 if self.applieddirty:
709 writelist(map(bytes, self.applied), self.statuspath)
709 writelist(map(bytes, self.applied), self.statuspath)
710 self.applieddirty = False
710 self.applieddirty = False
711 if self.seriesdirty:
711 if self.seriesdirty:
712 writelist(self.fullseries, self.seriespath)
712 writelist(self.fullseries, self.seriespath)
713 self.seriesdirty = False
713 self.seriesdirty = False
714 if self.guardsdirty:
714 if self.guardsdirty:
715 writelist(self.activeguards, self.guardspath)
715 writelist(self.activeguards, self.guardspath)
716 self.guardsdirty = False
716 self.guardsdirty = False
717 if self.added:
717 if self.added:
718 qrepo = self.qrepo()
718 qrepo = self.qrepo()
719 if qrepo:
719 if qrepo:
720 qrepo[None].add(f for f in self.added if f not in qrepo[None])
720 qrepo[None].add(f for f in self.added if f not in qrepo[None])
721 self.added = []
721 self.added = []
722
722
723 def removeundo(self, repo):
723 def removeundo(self, repo):
724 undo = repo.sjoin('undo')
724 undo = repo.sjoin('undo')
725 if not os.path.exists(undo):
725 if not os.path.exists(undo):
726 return
726 return
727 try:
727 try:
728 os.unlink(undo)
728 os.unlink(undo)
729 except OSError as inst:
729 except OSError as inst:
730 self.ui.warn(_('error removing undo: %s\n') %
730 self.ui.warn(_('error removing undo: %s\n') %
731 stringutil.forcebytestr(inst))
731 stringutil.forcebytestr(inst))
732
732
733 def backup(self, repo, files, copy=False):
733 def backup(self, repo, files, copy=False):
734 # backup local changes in --force case
734 # backup local changes in --force case
735 for f in sorted(files):
735 for f in sorted(files):
736 absf = repo.wjoin(f)
736 absf = repo.wjoin(f)
737 if os.path.lexists(absf):
737 if os.path.lexists(absf):
738 self.ui.note(_('saving current version of %s as %s\n') %
738 self.ui.note(_('saving current version of %s as %s\n') %
739 (f, scmutil.origpath(self.ui, repo, f)))
739 (f, scmutil.origpath(self.ui, repo, f)))
740
740
741 absorig = scmutil.origpath(self.ui, repo, absf)
741 absorig = scmutil.origpath(self.ui, repo, absf)
742 if copy:
742 if copy:
743 util.copyfile(absf, absorig)
743 util.copyfile(absf, absorig)
744 else:
744 else:
745 util.rename(absf, absorig)
745 util.rename(absf, absorig)
746
746
747 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
747 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
748 fp=None, changes=None, opts=None):
748 fp=None, changes=None, opts=None):
749 if opts is None:
749 if opts is None:
750 opts = {}
750 opts = {}
751 stat = opts.get('stat')
751 stat = opts.get('stat')
752 m = scmutil.match(repo[node1], files, opts)
752 m = scmutil.match(repo[node1], files, opts)
753 logcmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
753 logcmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
754 changes, stat, fp)
754 changes, stat, fp)
755
755
756 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
756 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
757 # first try just applying the patch
757 # first try just applying the patch
758 (err, n) = self.apply(repo, [patch], update_status=False,
758 (err, n) = self.apply(repo, [patch], update_status=False,
759 strict=True, merge=rev)
759 strict=True, merge=rev)
760
760
761 if err == 0:
761 if err == 0:
762 return (err, n)
762 return (err, n)
763
763
764 if n is None:
764 if n is None:
765 raise error.Abort(_("apply failed for patch %s") % patch)
765 raise error.Abort(_("apply failed for patch %s") % patch)
766
766
767 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
767 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
768
768
769 # apply failed, strip away that rev and merge.
769 # apply failed, strip away that rev and merge.
770 hg.clean(repo, head)
770 hg.clean(repo, head)
771 strip(self.ui, repo, [n], update=False, backup=False)
771 strip(self.ui, repo, [n], update=False, backup=False)
772
772
773 ctx = repo[rev]
773 ctx = repo[rev]
774 ret = hg.merge(repo, rev)
774 ret = hg.merge(repo, rev)
775 if ret:
775 if ret:
776 raise error.Abort(_("update returned %d") % ret)
776 raise error.Abort(_("update returned %d") % ret)
777 n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
777 n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
778 if n is None:
778 if n is None:
779 raise error.Abort(_("repo commit failed"))
779 raise error.Abort(_("repo commit failed"))
780 try:
780 try:
781 ph = patchheader(mergeq.join(patch), self.plainmode)
781 ph = patchheader(mergeq.join(patch), self.plainmode)
782 except Exception:
782 except Exception:
783 raise error.Abort(_("unable to read %s") % patch)
783 raise error.Abort(_("unable to read %s") % patch)
784
784
785 diffopts = self.patchopts(diffopts, patch)
785 diffopts = self.patchopts(diffopts, patch)
786 patchf = self.opener(patch, "w")
786 patchf = self.opener(patch, "w")
787 comments = bytes(ph)
787 comments = bytes(ph)
788 if comments:
788 if comments:
789 patchf.write(comments)
789 patchf.write(comments)
790 self.printdiff(repo, diffopts, head, n, fp=patchf)
790 self.printdiff(repo, diffopts, head, n, fp=patchf)
791 patchf.close()
791 patchf.close()
792 self.removeundo(repo)
792 self.removeundo(repo)
793 return (0, n)
793 return (0, n)
794
794
795 def qparents(self, repo, rev=None):
795 def qparents(self, repo, rev=None):
796 """return the mq handled parent or p1
796 """return the mq handled parent or p1
797
797
798 In some case where mq get himself in being the parent of a merge the
798 In some case where mq get himself in being the parent of a merge the
799 appropriate parent may be p2.
799 appropriate parent may be p2.
800 (eg: an in progress merge started with mq disabled)
800 (eg: an in progress merge started with mq disabled)
801
801
802 If no parent are managed by mq, p1 is returned.
802 If no parent are managed by mq, p1 is returned.
803 """
803 """
804 if rev is None:
804 if rev is None:
805 (p1, p2) = repo.dirstate.parents()
805 (p1, p2) = repo.dirstate.parents()
806 if p2 == nullid:
806 if p2 == nullid:
807 return p1
807 return p1
808 if not self.applied:
808 if not self.applied:
809 return None
809 return None
810 return self.applied[-1].node
810 return self.applied[-1].node
811 p1, p2 = repo.changelog.parents(rev)
811 p1, p2 = repo.changelog.parents(rev)
812 if p2 != nullid and p2 in [x.node for x in self.applied]:
812 if p2 != nullid and p2 in [x.node for x in self.applied]:
813 return p2
813 return p2
814 return p1
814 return p1
815
815
816 def mergepatch(self, repo, mergeq, series, diffopts):
816 def mergepatch(self, repo, mergeq, series, diffopts):
817 if not self.applied:
817 if not self.applied:
818 # each of the patches merged in will have two parents. This
818 # each of the patches merged in will have two parents. This
819 # can confuse the qrefresh, qdiff, and strip code because it
819 # can confuse the qrefresh, qdiff, and strip code because it
820 # needs to know which parent is actually in the patch queue.
820 # needs to know which parent is actually in the patch queue.
821 # so, we insert a merge marker with only one parent. This way
821 # so, we insert a merge marker with only one parent. This way
822 # the first patch in the queue is never a merge patch
822 # the first patch in the queue is never a merge patch
823 #
823 #
824 pname = ".hg.patches.merge.marker"
824 pname = ".hg.patches.merge.marker"
825 n = newcommit(repo, None, '[mq]: merge marker', force=True)
825 n = newcommit(repo, None, '[mq]: merge marker', force=True)
826 self.removeundo(repo)
826 self.removeundo(repo)
827 self.applied.append(statusentry(n, pname))
827 self.applied.append(statusentry(n, pname))
828 self.applieddirty = True
828 self.applieddirty = True
829
829
830 head = self.qparents(repo)
830 head = self.qparents(repo)
831
831
832 for patch in series:
832 for patch in series:
833 patch = mergeq.lookup(patch, strict=True)
833 patch = mergeq.lookup(patch, strict=True)
834 if not patch:
834 if not patch:
835 self.ui.warn(_("patch %s does not exist\n") % patch)
835 self.ui.warn(_("patch %s does not exist\n") % patch)
836 return (1, None)
836 return (1, None)
837 pushable, reason = self.pushable(patch)
837 pushable, reason = self.pushable(patch)
838 if not pushable:
838 if not pushable:
839 self.explainpushable(patch, all_patches=True)
839 self.explainpushable(patch, all_patches=True)
840 continue
840 continue
841 info = mergeq.isapplied(patch)
841 info = mergeq.isapplied(patch)
842 if not info:
842 if not info:
843 self.ui.warn(_("patch %s is not applied\n") % patch)
843 self.ui.warn(_("patch %s is not applied\n") % patch)
844 return (1, None)
844 return (1, None)
845 rev = info[1]
845 rev = info[1]
846 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
846 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
847 if head:
847 if head:
848 self.applied.append(statusentry(head, patch))
848 self.applied.append(statusentry(head, patch))
849 self.applieddirty = True
849 self.applieddirty = True
850 if err:
850 if err:
851 return (err, head)
851 return (err, head)
852 self.savedirty()
852 self.savedirty()
853 return (0, head)
853 return (0, head)
854
854
855 def patch(self, repo, patchfile):
855 def patch(self, repo, patchfile):
856 '''Apply patchfile to the working directory.
856 '''Apply patchfile to the working directory.
857 patchfile: name of patch file'''
857 patchfile: name of patch file'''
858 files = set()
858 files = set()
859 try:
859 try:
860 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
860 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
861 files=files, eolmode=None)
861 files=files, eolmode=None)
862 return (True, list(files), fuzz)
862 return (True, list(files), fuzz)
863 except Exception as inst:
863 except Exception as inst:
864 self.ui.note(stringutil.forcebytestr(inst) + '\n')
864 self.ui.note(stringutil.forcebytestr(inst) + '\n')
865 if not self.ui.verbose:
865 if not self.ui.verbose:
866 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
866 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
867 self.ui.traceback()
867 self.ui.traceback()
868 return (False, list(files), False)
868 return (False, list(files), False)
869
869
870 def apply(self, repo, series, list=False, update_status=True,
870 def apply(self, repo, series, list=False, update_status=True,
871 strict=False, patchdir=None, merge=None, all_files=None,
871 strict=False, patchdir=None, merge=None, all_files=None,
872 tobackup=None, keepchanges=False):
872 tobackup=None, keepchanges=False):
873 wlock = lock = tr = None
873 wlock = lock = tr = None
874 try:
874 try:
875 wlock = repo.wlock()
875 wlock = repo.wlock()
876 lock = repo.lock()
876 lock = repo.lock()
877 tr = repo.transaction("qpush")
877 tr = repo.transaction("qpush")
878 try:
878 try:
879 ret = self._apply(repo, series, list, update_status,
879 ret = self._apply(repo, series, list, update_status,
880 strict, patchdir, merge, all_files=all_files,
880 strict, patchdir, merge, all_files=all_files,
881 tobackup=tobackup, keepchanges=keepchanges)
881 tobackup=tobackup, keepchanges=keepchanges)
882 tr.close()
882 tr.close()
883 self.savedirty()
883 self.savedirty()
884 return ret
884 return ret
885 except AbortNoCleanup:
885 except AbortNoCleanup:
886 tr.close()
886 tr.close()
887 self.savedirty()
887 self.savedirty()
888 raise
888 raise
889 except: # re-raises
889 except: # re-raises
890 try:
890 try:
891 tr.abort()
891 tr.abort()
892 finally:
892 finally:
893 self.invalidate()
893 self.invalidate()
894 raise
894 raise
895 finally:
895 finally:
896 release(tr, lock, wlock)
896 release(tr, lock, wlock)
897 self.removeundo(repo)
897 self.removeundo(repo)
898
898
899 def _apply(self, repo, series, list=False, update_status=True,
899 def _apply(self, repo, series, list=False, update_status=True,
900 strict=False, patchdir=None, merge=None, all_files=None,
900 strict=False, patchdir=None, merge=None, all_files=None,
901 tobackup=None, keepchanges=False):
901 tobackup=None, keepchanges=False):
902 """returns (error, hash)
902 """returns (error, hash)
903
903
904 error = 1 for unable to read, 2 for patch failed, 3 for patch
904 error = 1 for unable to read, 2 for patch failed, 3 for patch
905 fuzz. tobackup is None or a set of files to backup before they
905 fuzz. tobackup is None or a set of files to backup before they
906 are modified by a patch.
906 are modified by a patch.
907 """
907 """
908 # TODO unify with commands.py
908 # TODO unify with commands.py
909 if not patchdir:
909 if not patchdir:
910 patchdir = self.path
910 patchdir = self.path
911 err = 0
911 err = 0
912 n = None
912 n = None
913 for patchname in series:
913 for patchname in series:
914 pushable, reason = self.pushable(patchname)
914 pushable, reason = self.pushable(patchname)
915 if not pushable:
915 if not pushable:
916 self.explainpushable(patchname, all_patches=True)
916 self.explainpushable(patchname, all_patches=True)
917 continue
917 continue
918 self.ui.status(_("applying %s\n") % patchname)
918 self.ui.status(_("applying %s\n") % patchname)
919 pf = os.path.join(patchdir, patchname)
919 pf = os.path.join(patchdir, patchname)
920
920
921 try:
921 try:
922 ph = patchheader(self.join(patchname), self.plainmode)
922 ph = patchheader(self.join(patchname), self.plainmode)
923 except IOError:
923 except IOError:
924 self.ui.warn(_("unable to read %s\n") % patchname)
924 self.ui.warn(_("unable to read %s\n") % patchname)
925 err = 1
925 err = 1
926 break
926 break
927
927
928 message = ph.message
928 message = ph.message
929 if not message:
929 if not message:
930 # The commit message should not be translated
930 # The commit message should not be translated
931 message = "imported patch %s\n" % patchname
931 message = "imported patch %s\n" % patchname
932 else:
932 else:
933 if list:
933 if list:
934 # The commit message should not be translated
934 # The commit message should not be translated
935 message.append("\nimported patch %s" % patchname)
935 message.append("\nimported patch %s" % patchname)
936 message = '\n'.join(message)
936 message = '\n'.join(message)
937
937
938 if ph.haspatch:
938 if ph.haspatch:
939 if tobackup:
939 if tobackup:
940 touched = patchmod.changedfiles(self.ui, repo, pf)
940 touched = patchmod.changedfiles(self.ui, repo, pf)
941 touched = set(touched) & tobackup
941 touched = set(touched) & tobackup
942 if touched and keepchanges:
942 if touched and keepchanges:
943 raise AbortNoCleanup(
943 raise AbortNoCleanup(
944 _("conflicting local changes found"),
944 _("conflicting local changes found"),
945 hint=_("did you forget to qrefresh?"))
945 hint=_("did you forget to qrefresh?"))
946 self.backup(repo, touched, copy=True)
946 self.backup(repo, touched, copy=True)
947 tobackup = tobackup - touched
947 tobackup = tobackup - touched
948 (patcherr, files, fuzz) = self.patch(repo, pf)
948 (patcherr, files, fuzz) = self.patch(repo, pf)
949 if all_files is not None:
949 if all_files is not None:
950 all_files.update(files)
950 all_files.update(files)
951 patcherr = not patcherr
951 patcherr = not patcherr
952 else:
952 else:
953 self.ui.warn(_("patch %s is empty\n") % patchname)
953 self.ui.warn(_("patch %s is empty\n") % patchname)
954 patcherr, files, fuzz = 0, [], 0
954 patcherr, files, fuzz = 0, [], 0
955
955
956 if merge and files:
956 if merge and files:
957 # Mark as removed/merged and update dirstate parent info
957 # Mark as removed/merged and update dirstate parent info
958 removed = []
958 removed = []
959 merged = []
959 merged = []
960 for f in files:
960 for f in files:
961 if os.path.lexists(repo.wjoin(f)):
961 if os.path.lexists(repo.wjoin(f)):
962 merged.append(f)
962 merged.append(f)
963 else:
963 else:
964 removed.append(f)
964 removed.append(f)
965 with repo.dirstate.parentchange():
965 with repo.dirstate.parentchange():
966 for f in removed:
966 for f in removed:
967 repo.dirstate.remove(f)
967 repo.dirstate.remove(f)
968 for f in merged:
968 for f in merged:
969 repo.dirstate.merge(f)
969 repo.dirstate.merge(f)
970 p1, p2 = repo.dirstate.parents()
970 p1, p2 = repo.dirstate.parents()
971 repo.setparents(p1, merge)
971 repo.setparents(p1, merge)
972
972
973 if all_files and '.hgsubstate' in all_files:
973 if all_files and '.hgsubstate' in all_files:
974 wctx = repo[None]
974 wctx = repo[None]
975 pctx = repo['.']
975 pctx = repo['.']
976 overwrite = False
976 overwrite = False
977 mergedsubstate = subrepoutil.submerge(repo, pctx, wctx, wctx,
977 mergedsubstate = subrepoutil.submerge(repo, pctx, wctx, wctx,
978 overwrite)
978 overwrite)
979 files += mergedsubstate.keys()
979 files += mergedsubstate.keys()
980
980
981 match = scmutil.matchfiles(repo, files or [])
981 match = scmutil.matchfiles(repo, files or [])
982 oldtip = repo['tip']
982 oldtip = repo['tip']
983 n = newcommit(repo, None, message, ph.user, ph.date, match=match,
983 n = newcommit(repo, None, message, ph.user, ph.date, match=match,
984 force=True)
984 force=True)
985 if repo['tip'] == oldtip:
985 if repo['tip'] == oldtip:
986 raise error.Abort(_("qpush exactly duplicates child changeset"))
986 raise error.Abort(_("qpush exactly duplicates child changeset"))
987 if n is None:
987 if n is None:
988 raise error.Abort(_("repository commit failed"))
988 raise error.Abort(_("repository commit failed"))
989
989
990 if update_status:
990 if update_status:
991 self.applied.append(statusentry(n, patchname))
991 self.applied.append(statusentry(n, patchname))
992
992
993 if patcherr:
993 if patcherr:
994 self.ui.warn(_("patch failed, rejects left in working "
994 self.ui.warn(_("patch failed, rejects left in working "
995 "directory\n"))
995 "directory\n"))
996 err = 2
996 err = 2
997 break
997 break
998
998
999 if fuzz and strict:
999 if fuzz and strict:
1000 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
1000 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
1001 err = 3
1001 err = 3
1002 break
1002 break
1003 return (err, n)
1003 return (err, n)
1004
1004
1005 def _cleanup(self, patches, numrevs, keep=False):
1005 def _cleanup(self, patches, numrevs, keep=False):
1006 if not keep:
1006 if not keep:
1007 r = self.qrepo()
1007 r = self.qrepo()
1008 if r:
1008 if r:
1009 r[None].forget(patches)
1009 r[None].forget(patches)
1010 for p in patches:
1010 for p in patches:
1011 try:
1011 try:
1012 os.unlink(self.join(p))
1012 os.unlink(self.join(p))
1013 except OSError as inst:
1013 except OSError as inst:
1014 if inst.errno != errno.ENOENT:
1014 if inst.errno != errno.ENOENT:
1015 raise
1015 raise
1016
1016
1017 qfinished = []
1017 qfinished = []
1018 if numrevs:
1018 if numrevs:
1019 qfinished = self.applied[:numrevs]
1019 qfinished = self.applied[:numrevs]
1020 del self.applied[:numrevs]
1020 del self.applied[:numrevs]
1021 self.applieddirty = True
1021 self.applieddirty = True
1022
1022
1023 unknown = []
1023 unknown = []
1024
1024
1025 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 % 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 patchf = self.opener(patchname, "w")
2185 patchf = self.opener(patchname, "w")
2186 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
2186 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
2187 patchf.close()
2187 patchf.close()
2188
2188
2189 se = statusentry(n, patchname)
2189 se = statusentry(n, patchname)
2190 self.applied.insert(0, se)
2190 self.applied.insert(0, se)
2191
2191
2192 self.added.append(patchname)
2192 self.added.append(patchname)
2193 imported.append(patchname)
2193 imported.append(patchname)
2194 patchname = None
2194 patchname = None
2195 if rev and repo.ui.configbool('mq', 'secret'):
2195 if rev and repo.ui.configbool('mq', 'secret'):
2196 # if we added anything with --rev, move the secret root
2196 # if we added anything with --rev, move the secret root
2197 phases.retractboundary(repo, tr, phases.secret, [n])
2197 phases.retractboundary(repo, tr, phases.secret, [n])
2198 self.parseseries()
2198 self.parseseries()
2199 self.applieddirty = True
2199 self.applieddirty = True
2200 self.seriesdirty = True
2200 self.seriesdirty = True
2201
2201
2202 for i, filename in enumerate(files):
2202 for i, filename in enumerate(files):
2203 if existing:
2203 if existing:
2204 if filename == '-':
2204 if filename == '-':
2205 raise error.Abort(_('-e is incompatible with import from -')
2205 raise error.Abort(_('-e is incompatible with import from -')
2206 )
2206 )
2207 filename = normname(filename)
2207 filename = normname(filename)
2208 self.checkreservedname(filename)
2208 self.checkreservedname(filename)
2209 if util.url(filename).islocal():
2209 if util.url(filename).islocal():
2210 originpath = self.join(filename)
2210 originpath = self.join(filename)
2211 if not os.path.isfile(originpath):
2211 if not os.path.isfile(originpath):
2212 raise error.Abort(
2212 raise error.Abort(
2213 _("patch %s does not exist") % filename)
2213 _("patch %s does not exist") % filename)
2214
2214
2215 if patchname:
2215 if patchname:
2216 self.checkpatchname(patchname, force)
2216 self.checkpatchname(patchname, force)
2217
2217
2218 self.ui.write(_('renaming %s to %s\n')
2218 self.ui.write(_('renaming %s to %s\n')
2219 % (filename, patchname))
2219 % (filename, patchname))
2220 util.rename(originpath, self.join(patchname))
2220 util.rename(originpath, self.join(patchname))
2221 else:
2221 else:
2222 patchname = filename
2222 patchname = filename
2223
2223
2224 else:
2224 else:
2225 if filename == '-' and not patchname:
2225 if filename == '-' and not patchname:
2226 raise error.Abort(_('need --name to import a patch from -'))
2226 raise error.Abort(_('need --name to import a patch from -'))
2227 elif not patchname:
2227 elif not patchname:
2228 patchname = normname(os.path.basename(filename.rstrip('/')))
2228 patchname = normname(os.path.basename(filename.rstrip('/')))
2229 self.checkpatchname(patchname, force)
2229 self.checkpatchname(patchname, force)
2230 try:
2230 try:
2231 if filename == '-':
2231 if filename == '-':
2232 text = self.ui.fin.read()
2232 text = self.ui.fin.read()
2233 else:
2233 else:
2234 fp = hg.openpath(self.ui, filename)
2234 fp = hg.openpath(self.ui, filename)
2235 text = fp.read()
2235 text = fp.read()
2236 fp.close()
2236 fp.close()
2237 except (OSError, IOError):
2237 except (OSError, IOError):
2238 raise error.Abort(_("unable to read file %s") % filename)
2238 raise error.Abort(_("unable to read file %s") % filename)
2239 patchf = self.opener(patchname, "w")
2239 patchf = self.opener(patchname, "w")
2240 patchf.write(text)
2240 patchf.write(text)
2241 patchf.close()
2241 patchf.close()
2242 if not force:
2242 if not force:
2243 checkseries(patchname)
2243 checkseries(patchname)
2244 if patchname not in self.series:
2244 if patchname not in self.series:
2245 index = self.fullseriesend() + i
2245 index = self.fullseriesend() + i
2246 self.fullseries[index:index] = [patchname]
2246 self.fullseries[index:index] = [patchname]
2247 self.parseseries()
2247 self.parseseries()
2248 self.seriesdirty = True
2248 self.seriesdirty = True
2249 self.ui.warn(_("adding %s to series file\n") % patchname)
2249 self.ui.warn(_("adding %s to series file\n") % patchname)
2250 self.added.append(patchname)
2250 self.added.append(patchname)
2251 imported.append(patchname)
2251 imported.append(patchname)
2252 patchname = None
2252 patchname = None
2253
2253
2254 self.removeundo(repo)
2254 self.removeundo(repo)
2255 return imported
2255 return imported
2256
2256
2257 def fixkeepchangesopts(ui, opts):
2257 def fixkeepchangesopts(ui, opts):
2258 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2258 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2259 or opts.get('exact')):
2259 or opts.get('exact')):
2260 return opts
2260 return opts
2261 opts = dict(opts)
2261 opts = dict(opts)
2262 opts['keep_changes'] = True
2262 opts['keep_changes'] = True
2263 return opts
2263 return opts
2264
2264
2265 @command("qdelete|qremove|qrm",
2265 @command("qdelete|qremove|qrm",
2266 [('k', 'keep', None, _('keep patch file')),
2266 [('k', 'keep', None, _('keep patch file')),
2267 ('r', 'rev', [],
2267 ('r', 'rev', [],
2268 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2268 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2269 _('hg qdelete [-k] [PATCH]...'))
2269 _('hg qdelete [-k] [PATCH]...'))
2270 def delete(ui, repo, *patches, **opts):
2270 def delete(ui, repo, *patches, **opts):
2271 """remove patches from queue
2271 """remove patches from queue
2272
2272
2273 The patches must not be applied, and at least one patch is required. Exact
2273 The patches must not be applied, and at least one patch is required. Exact
2274 patch identifiers must be given. With -k/--keep, the patch files are
2274 patch identifiers must be given. With -k/--keep, the patch files are
2275 preserved in the patch directory.
2275 preserved in the patch directory.
2276
2276
2277 To stop managing a patch and move it into permanent history,
2277 To stop managing a patch and move it into permanent history,
2278 use the :hg:`qfinish` command."""
2278 use the :hg:`qfinish` command."""
2279 q = repo.mq
2279 q = repo.mq
2280 q.delete(repo, patches, pycompat.byteskwargs(opts))
2280 q.delete(repo, patches, pycompat.byteskwargs(opts))
2281 q.savedirty()
2281 q.savedirty()
2282 return 0
2282 return 0
2283
2283
2284 @command("qapplied",
2284 @command("qapplied",
2285 [('1', 'last', None, _('show only the preceding applied patch'))
2285 [('1', 'last', None, _('show only the preceding applied patch'))
2286 ] + seriesopts,
2286 ] + seriesopts,
2287 _('hg qapplied [-1] [-s] [PATCH]'))
2287 _('hg qapplied [-1] [-s] [PATCH]'))
2288 def applied(ui, repo, patch=None, **opts):
2288 def applied(ui, repo, patch=None, **opts):
2289 """print the patches already applied
2289 """print the patches already applied
2290
2290
2291 Returns 0 on success."""
2291 Returns 0 on success."""
2292
2292
2293 q = repo.mq
2293 q = repo.mq
2294 opts = pycompat.byteskwargs(opts)
2294 opts = pycompat.byteskwargs(opts)
2295
2295
2296 if patch:
2296 if patch:
2297 if patch not in q.series:
2297 if patch not in q.series:
2298 raise error.Abort(_("patch %s is not in series file") % patch)
2298 raise error.Abort(_("patch %s is not in series file") % patch)
2299 end = q.series.index(patch) + 1
2299 end = q.series.index(patch) + 1
2300 else:
2300 else:
2301 end = q.seriesend(True)
2301 end = q.seriesend(True)
2302
2302
2303 if opts.get('last') and not end:
2303 if opts.get('last') and not end:
2304 ui.write(_("no patches applied\n"))
2304 ui.write(_("no patches applied\n"))
2305 return 1
2305 return 1
2306 elif opts.get('last') and end == 1:
2306 elif opts.get('last') and end == 1:
2307 ui.write(_("only one patch applied\n"))
2307 ui.write(_("only one patch applied\n"))
2308 return 1
2308 return 1
2309 elif opts.get('last'):
2309 elif opts.get('last'):
2310 start = end - 2
2310 start = end - 2
2311 end = 1
2311 end = 1
2312 else:
2312 else:
2313 start = 0
2313 start = 0
2314
2314
2315 q.qseries(repo, length=end, start=start, status='A',
2315 q.qseries(repo, length=end, start=start, status='A',
2316 summary=opts.get('summary'))
2316 summary=opts.get('summary'))
2317
2317
2318
2318
2319 @command("qunapplied",
2319 @command("qunapplied",
2320 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2320 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2321 _('hg qunapplied [-1] [-s] [PATCH]'))
2321 _('hg qunapplied [-1] [-s] [PATCH]'))
2322 def unapplied(ui, repo, patch=None, **opts):
2322 def unapplied(ui, repo, patch=None, **opts):
2323 """print the patches not yet applied
2323 """print the patches not yet applied
2324
2324
2325 Returns 0 on success."""
2325 Returns 0 on success."""
2326
2326
2327 q = repo.mq
2327 q = repo.mq
2328 opts = pycompat.byteskwargs(opts)
2328 opts = pycompat.byteskwargs(opts)
2329 if patch:
2329 if patch:
2330 if patch not in q.series:
2330 if patch not in q.series:
2331 raise error.Abort(_("patch %s is not in series file") % patch)
2331 raise error.Abort(_("patch %s is not in series file") % patch)
2332 start = q.series.index(patch) + 1
2332 start = q.series.index(patch) + 1
2333 else:
2333 else:
2334 start = q.seriesend(True)
2334 start = q.seriesend(True)
2335
2335
2336 if start == len(q.series) and opts.get('first'):
2336 if start == len(q.series) and opts.get('first'):
2337 ui.write(_("all patches applied\n"))
2337 ui.write(_("all patches applied\n"))
2338 return 1
2338 return 1
2339
2339
2340 if opts.get('first'):
2340 if opts.get('first'):
2341 length = 1
2341 length = 1
2342 else:
2342 else:
2343 length = None
2343 length = None
2344 q.qseries(repo, start=start, length=length, status='U',
2344 q.qseries(repo, start=start, length=length, status='U',
2345 summary=opts.get('summary'))
2345 summary=opts.get('summary'))
2346
2346
2347 @command("qimport",
2347 @command("qimport",
2348 [('e', 'existing', None, _('import file in patch directory')),
2348 [('e', 'existing', None, _('import file in patch directory')),
2349 ('n', 'name', '',
2349 ('n', 'name', '',
2350 _('name of patch file'), _('NAME')),
2350 _('name of patch file'), _('NAME')),
2351 ('f', 'force', None, _('overwrite existing files')),
2351 ('f', 'force', None, _('overwrite existing files')),
2352 ('r', 'rev', [],
2352 ('r', 'rev', [],
2353 _('place existing revisions under mq control'), _('REV')),
2353 _('place existing revisions under mq control'), _('REV')),
2354 ('g', 'git', None, _('use git extended diff format')),
2354 ('g', 'git', None, _('use git extended diff format')),
2355 ('P', 'push', None, _('qpush after importing'))],
2355 ('P', 'push', None, _('qpush after importing'))],
2356 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2356 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2357 def qimport(ui, repo, *filename, **opts):
2357 def qimport(ui, repo, *filename, **opts):
2358 """import a patch or existing changeset
2358 """import a patch or existing changeset
2359
2359
2360 The patch is inserted into the series after the last applied
2360 The patch is inserted into the series after the last applied
2361 patch. If no patches have been applied, qimport prepends the patch
2361 patch. If no patches have been applied, qimport prepends the patch
2362 to the series.
2362 to the series.
2363
2363
2364 The patch will have the same name as its source file unless you
2364 The patch will have the same name as its source file unless you
2365 give it a new one with -n/--name.
2365 give it a new one with -n/--name.
2366
2366
2367 You can register an existing patch inside the patch directory with
2367 You can register an existing patch inside the patch directory with
2368 the -e/--existing flag.
2368 the -e/--existing flag.
2369
2369
2370 With -f/--force, an existing patch of the same name will be
2370 With -f/--force, an existing patch of the same name will be
2371 overwritten.
2371 overwritten.
2372
2372
2373 An existing changeset may be placed under mq control with -r/--rev
2373 An existing changeset may be placed under mq control with -r/--rev
2374 (e.g. qimport --rev . -n patch will place the current revision
2374 (e.g. qimport --rev . -n patch will place the current revision
2375 under mq control). With -g/--git, patches imported with --rev will
2375 under mq control). With -g/--git, patches imported with --rev will
2376 use the git diff format. See the diffs help topic for information
2376 use the git diff format. See the diffs help topic for information
2377 on why this is important for preserving rename/copy information
2377 on why this is important for preserving rename/copy information
2378 and permission changes. Use :hg:`qfinish` to remove changesets
2378 and permission changes. Use :hg:`qfinish` to remove changesets
2379 from mq control.
2379 from mq control.
2380
2380
2381 To import a patch from standard input, pass - as the patch file.
2381 To import a patch from standard input, pass - as the patch file.
2382 When importing from standard input, a patch name must be specified
2382 When importing from standard input, a patch name must be specified
2383 using the --name flag.
2383 using the --name flag.
2384
2384
2385 To import an existing patch while renaming it::
2385 To import an existing patch while renaming it::
2386
2386
2387 hg qimport -e existing-patch -n new-name
2387 hg qimport -e existing-patch -n new-name
2388
2388
2389 Returns 0 if import succeeded.
2389 Returns 0 if import succeeded.
2390 """
2390 """
2391 opts = pycompat.byteskwargs(opts)
2391 opts = pycompat.byteskwargs(opts)
2392 with repo.lock(): # cause this may move phase
2392 with repo.lock(): # cause this may move phase
2393 q = repo.mq
2393 q = repo.mq
2394 try:
2394 try:
2395 imported = q.qimport(
2395 imported = q.qimport(
2396 repo, filename, patchname=opts.get('name'),
2396 repo, filename, patchname=opts.get('name'),
2397 existing=opts.get('existing'), force=opts.get('force'),
2397 existing=opts.get('existing'), force=opts.get('force'),
2398 rev=opts.get('rev'), git=opts.get('git'))
2398 rev=opts.get('rev'), git=opts.get('git'))
2399 finally:
2399 finally:
2400 q.savedirty()
2400 q.savedirty()
2401
2401
2402 if imported and opts.get('push') and not opts.get('rev'):
2402 if imported and opts.get('push') and not opts.get('rev'):
2403 return q.push(repo, imported[-1])
2403 return q.push(repo, imported[-1])
2404 return 0
2404 return 0
2405
2405
2406 def qinit(ui, repo, create):
2406 def qinit(ui, repo, create):
2407 """initialize a new queue repository
2407 """initialize a new queue repository
2408
2408
2409 This command also creates a series file for ordering patches, and
2409 This command also creates a series file for ordering patches, and
2410 an mq-specific .hgignore file in the queue repository, to exclude
2410 an mq-specific .hgignore file in the queue repository, to exclude
2411 the status and guards files (these contain mostly transient state).
2411 the status and guards files (these contain mostly transient state).
2412
2412
2413 Returns 0 if initialization succeeded."""
2413 Returns 0 if initialization succeeded."""
2414 q = repo.mq
2414 q = repo.mq
2415 r = q.init(repo, create)
2415 r = q.init(repo, create)
2416 q.savedirty()
2416 q.savedirty()
2417 if r:
2417 if r:
2418 if not os.path.exists(r.wjoin('.hgignore')):
2418 if not os.path.exists(r.wjoin('.hgignore')):
2419 fp = r.wvfs('.hgignore', 'w')
2419 fp = r.wvfs('.hgignore', 'w')
2420 fp.write('^\\.hg\n')
2420 fp.write('^\\.hg\n')
2421 fp.write('^\\.mq\n')
2421 fp.write('^\\.mq\n')
2422 fp.write('syntax: glob\n')
2422 fp.write('syntax: glob\n')
2423 fp.write('status\n')
2423 fp.write('status\n')
2424 fp.write('guards\n')
2424 fp.write('guards\n')
2425 fp.close()
2425 fp.close()
2426 if not os.path.exists(r.wjoin('series')):
2426 if not os.path.exists(r.wjoin('series')):
2427 r.wvfs('series', 'w').close()
2427 r.wvfs('series', 'w').close()
2428 r[None].add(['.hgignore', 'series'])
2428 r[None].add(['.hgignore', 'series'])
2429 commands.add(ui, r)
2429 commands.add(ui, r)
2430 return 0
2430 return 0
2431
2431
2432 @command("^qinit",
2432 @command("^qinit",
2433 [('c', 'create-repo', None, _('create queue repository'))],
2433 [('c', 'create-repo', None, _('create queue repository'))],
2434 _('hg qinit [-c]'))
2434 _('hg qinit [-c]'))
2435 def init(ui, repo, **opts):
2435 def init(ui, repo, **opts):
2436 """init a new queue repository (DEPRECATED)
2436 """init a new queue repository (DEPRECATED)
2437
2437
2438 The queue repository is unversioned by default. If
2438 The queue repository is unversioned by default. If
2439 -c/--create-repo is specified, qinit will create a separate nested
2439 -c/--create-repo is specified, qinit will create a separate nested
2440 repository for patches (qinit -c may also be run later to convert
2440 repository for patches (qinit -c may also be run later to convert
2441 an unversioned patch repository into a versioned one). You can use
2441 an unversioned patch repository into a versioned one). You can use
2442 qcommit to commit changes to this queue repository.
2442 qcommit to commit changes to this queue repository.
2443
2443
2444 This command is deprecated. Without -c, it's implied by other relevant
2444 This command is deprecated. Without -c, it's implied by other relevant
2445 commands. With -c, use :hg:`init --mq` instead."""
2445 commands. With -c, use :hg:`init --mq` instead."""
2446 return qinit(ui, repo, create=opts.get(r'create_repo'))
2446 return qinit(ui, repo, create=opts.get(r'create_repo'))
2447
2447
2448 @command("qclone",
2448 @command("qclone",
2449 [('', 'pull', None, _('use pull protocol to copy metadata')),
2449 [('', 'pull', None, _('use pull protocol to copy metadata')),
2450 ('U', 'noupdate', None,
2450 ('U', 'noupdate', None,
2451 _('do not update the new working directories')),
2451 _('do not update the new working directories')),
2452 ('', 'uncompressed', None,
2452 ('', 'uncompressed', None,
2453 _('use uncompressed transfer (fast over LAN)')),
2453 _('use uncompressed transfer (fast over LAN)')),
2454 ('p', 'patches', '',
2454 ('p', 'patches', '',
2455 _('location of source patch repository'), _('REPO')),
2455 _('location of source patch repository'), _('REPO')),
2456 ] + cmdutil.remoteopts,
2456 ] + cmdutil.remoteopts,
2457 _('hg qclone [OPTION]... SOURCE [DEST]'),
2457 _('hg qclone [OPTION]... SOURCE [DEST]'),
2458 norepo=True)
2458 norepo=True)
2459 def clone(ui, source, dest=None, **opts):
2459 def clone(ui, source, dest=None, **opts):
2460 '''clone main and patch repository at same time
2460 '''clone main and patch repository at same time
2461
2461
2462 If source is local, destination will have no patches applied. If
2462 If source is local, destination will have no patches applied. If
2463 source is remote, this command can not check if patches are
2463 source is remote, this command can not check if patches are
2464 applied in source, so cannot guarantee that patches are not
2464 applied in source, so cannot guarantee that patches are not
2465 applied in destination. If you clone remote repository, be sure
2465 applied in destination. If you clone remote repository, be sure
2466 before that it has no patches applied.
2466 before that it has no patches applied.
2467
2467
2468 Source patch repository is looked for in <src>/.hg/patches by
2468 Source patch repository is looked for in <src>/.hg/patches by
2469 default. Use -p <url> to change.
2469 default. Use -p <url> to change.
2470
2470
2471 The patch directory must be a nested Mercurial repository, as
2471 The patch directory must be a nested Mercurial repository, as
2472 would be created by :hg:`init --mq`.
2472 would be created by :hg:`init --mq`.
2473
2473
2474 Return 0 on success.
2474 Return 0 on success.
2475 '''
2475 '''
2476 opts = pycompat.byteskwargs(opts)
2476 opts = pycompat.byteskwargs(opts)
2477 def patchdir(repo):
2477 def patchdir(repo):
2478 """compute a patch repo url from a repo object"""
2478 """compute a patch repo url from a repo object"""
2479 url = repo.url()
2479 url = repo.url()
2480 if url.endswith('/'):
2480 if url.endswith('/'):
2481 url = url[:-1]
2481 url = url[:-1]
2482 return url + '/.hg/patches'
2482 return url + '/.hg/patches'
2483
2483
2484 # main repo (destination and sources)
2484 # main repo (destination and sources)
2485 if dest is None:
2485 if dest is None:
2486 dest = hg.defaultdest(source)
2486 dest = hg.defaultdest(source)
2487 sr = hg.peer(ui, opts, ui.expandpath(source))
2487 sr = hg.peer(ui, opts, ui.expandpath(source))
2488
2488
2489 # patches repo (source only)
2489 # patches repo (source only)
2490 if opts.get('patches'):
2490 if opts.get('patches'):
2491 patchespath = ui.expandpath(opts.get('patches'))
2491 patchespath = ui.expandpath(opts.get('patches'))
2492 else:
2492 else:
2493 patchespath = patchdir(sr)
2493 patchespath = patchdir(sr)
2494 try:
2494 try:
2495 hg.peer(ui, opts, patchespath)
2495 hg.peer(ui, opts, patchespath)
2496 except error.RepoError:
2496 except error.RepoError:
2497 raise error.Abort(_('versioned patch repository not found'
2497 raise error.Abort(_('versioned patch repository not found'
2498 ' (see init --mq)'))
2498 ' (see init --mq)'))
2499 qbase, destrev = None, None
2499 qbase, destrev = None, None
2500 if sr.local():
2500 if sr.local():
2501 repo = sr.local()
2501 repo = sr.local()
2502 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2502 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2503 qbase = repo.mq.applied[0].node
2503 qbase = repo.mq.applied[0].node
2504 if not hg.islocal(dest):
2504 if not hg.islocal(dest):
2505 heads = set(repo.heads())
2505 heads = set(repo.heads())
2506 destrev = list(heads.difference(repo.heads(qbase)))
2506 destrev = list(heads.difference(repo.heads(qbase)))
2507 destrev.append(repo.changelog.parents(qbase)[0])
2507 destrev.append(repo.changelog.parents(qbase)[0])
2508 elif sr.capable('lookup'):
2508 elif sr.capable('lookup'):
2509 try:
2509 try:
2510 qbase = sr.lookup('qbase')
2510 qbase = sr.lookup('qbase')
2511 except error.RepoError:
2511 except error.RepoError:
2512 pass
2512 pass
2513
2513
2514 ui.note(_('cloning main repository\n'))
2514 ui.note(_('cloning main repository\n'))
2515 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2515 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2516 pull=opts.get('pull'),
2516 pull=opts.get('pull'),
2517 revs=destrev,
2517 revs=destrev,
2518 update=False,
2518 update=False,
2519 stream=opts.get('uncompressed'))
2519 stream=opts.get('uncompressed'))
2520
2520
2521 ui.note(_('cloning patch repository\n'))
2521 ui.note(_('cloning patch repository\n'))
2522 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2522 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2523 pull=opts.get('pull'), update=not opts.get('noupdate'),
2523 pull=opts.get('pull'), update=not opts.get('noupdate'),
2524 stream=opts.get('uncompressed'))
2524 stream=opts.get('uncompressed'))
2525
2525
2526 if dr.local():
2526 if dr.local():
2527 repo = dr.local()
2527 repo = dr.local()
2528 if qbase:
2528 if qbase:
2529 ui.note(_('stripping applied patches from destination '
2529 ui.note(_('stripping applied patches from destination '
2530 'repository\n'))
2530 'repository\n'))
2531 strip(ui, repo, [qbase], update=False, backup=None)
2531 strip(ui, repo, [qbase], update=False, backup=None)
2532 if not opts.get('noupdate'):
2532 if not opts.get('noupdate'):
2533 ui.note(_('updating destination repository\n'))
2533 ui.note(_('updating destination repository\n'))
2534 hg.update(repo, repo.changelog.tip())
2534 hg.update(repo, repo.changelog.tip())
2535
2535
2536 @command("qcommit|qci",
2536 @command("qcommit|qci",
2537 commands.table["^commit|ci"][1],
2537 commands.table["^commit|ci"][1],
2538 _('hg qcommit [OPTION]... [FILE]...'),
2538 _('hg qcommit [OPTION]... [FILE]...'),
2539 inferrepo=True)
2539 inferrepo=True)
2540 def commit(ui, repo, *pats, **opts):
2540 def commit(ui, repo, *pats, **opts):
2541 """commit changes in the queue repository (DEPRECATED)
2541 """commit changes in the queue repository (DEPRECATED)
2542
2542
2543 This command is deprecated; use :hg:`commit --mq` instead."""
2543 This command is deprecated; use :hg:`commit --mq` instead."""
2544 q = repo.mq
2544 q = repo.mq
2545 r = q.qrepo()
2545 r = q.qrepo()
2546 if not r:
2546 if not r:
2547 raise error.Abort('no queue repository')
2547 raise error.Abort('no queue repository')
2548 commands.commit(r.ui, r, *pats, **opts)
2548 commands.commit(r.ui, r, *pats, **opts)
2549
2549
2550 @command("qseries",
2550 @command("qseries",
2551 [('m', 'missing', None, _('print patches not in series')),
2551 [('m', 'missing', None, _('print patches not in series')),
2552 ] + seriesopts,
2552 ] + seriesopts,
2553 _('hg qseries [-ms]'))
2553 _('hg qseries [-ms]'))
2554 def series(ui, repo, **opts):
2554 def series(ui, repo, **opts):
2555 """print the entire series file
2555 """print the entire series file
2556
2556
2557 Returns 0 on success."""
2557 Returns 0 on success."""
2558 repo.mq.qseries(repo, missing=opts.get(r'missing'),
2558 repo.mq.qseries(repo, missing=opts.get(r'missing'),
2559 summary=opts.get(r'summary'))
2559 summary=opts.get(r'summary'))
2560 return 0
2560 return 0
2561
2561
2562 @command("qtop", seriesopts, _('hg qtop [-s]'))
2562 @command("qtop", seriesopts, _('hg qtop [-s]'))
2563 def top(ui, repo, **opts):
2563 def top(ui, repo, **opts):
2564 """print the name of the current patch
2564 """print the name of the current patch
2565
2565
2566 Returns 0 on success."""
2566 Returns 0 on success."""
2567 q = repo.mq
2567 q = repo.mq
2568 if q.applied:
2568 if q.applied:
2569 t = q.seriesend(True)
2569 t = q.seriesend(True)
2570 else:
2570 else:
2571 t = 0
2571 t = 0
2572
2572
2573 if t:
2573 if t:
2574 q.qseries(repo, start=t - 1, length=1, status='A',
2574 q.qseries(repo, start=t - 1, length=1, status='A',
2575 summary=opts.get(r'summary'))
2575 summary=opts.get(r'summary'))
2576 else:
2576 else:
2577 ui.write(_("no patches applied\n"))
2577 ui.write(_("no patches applied\n"))
2578 return 1
2578 return 1
2579
2579
2580 @command("qnext", seriesopts, _('hg qnext [-s]'))
2580 @command("qnext", seriesopts, _('hg qnext [-s]'))
2581 def next(ui, repo, **opts):
2581 def next(ui, repo, **opts):
2582 """print the name of the next pushable patch
2582 """print the name of the next pushable patch
2583
2583
2584 Returns 0 on success."""
2584 Returns 0 on success."""
2585 q = repo.mq
2585 q = repo.mq
2586 end = q.seriesend()
2586 end = q.seriesend()
2587 if end == len(q.series):
2587 if end == len(q.series):
2588 ui.write(_("all patches applied\n"))
2588 ui.write(_("all patches applied\n"))
2589 return 1
2589 return 1
2590 q.qseries(repo, start=end, length=1, summary=opts.get(r'summary'))
2590 q.qseries(repo, start=end, length=1, summary=opts.get(r'summary'))
2591
2591
2592 @command("qprev", seriesopts, _('hg qprev [-s]'))
2592 @command("qprev", seriesopts, _('hg qprev [-s]'))
2593 def prev(ui, repo, **opts):
2593 def prev(ui, repo, **opts):
2594 """print the name of the preceding applied patch
2594 """print the name of the preceding applied patch
2595
2595
2596 Returns 0 on success."""
2596 Returns 0 on success."""
2597 q = repo.mq
2597 q = repo.mq
2598 l = len(q.applied)
2598 l = len(q.applied)
2599 if l == 1:
2599 if l == 1:
2600 ui.write(_("only one patch applied\n"))
2600 ui.write(_("only one patch applied\n"))
2601 return 1
2601 return 1
2602 if not l:
2602 if not l:
2603 ui.write(_("no patches applied\n"))
2603 ui.write(_("no patches applied\n"))
2604 return 1
2604 return 1
2605 idx = q.series.index(q.applied[-2].name)
2605 idx = q.series.index(q.applied[-2].name)
2606 q.qseries(repo, start=idx, length=1, status='A',
2606 q.qseries(repo, start=idx, length=1, status='A',
2607 summary=opts.get(r'summary'))
2607 summary=opts.get(r'summary'))
2608
2608
2609 def setupheaderopts(ui, opts):
2609 def setupheaderopts(ui, opts):
2610 if not opts.get('user') and opts.get('currentuser'):
2610 if not opts.get('user') and opts.get('currentuser'):
2611 opts['user'] = ui.username()
2611 opts['user'] = ui.username()
2612 if not opts.get('date') and opts.get('currentdate'):
2612 if not opts.get('date') and opts.get('currentdate'):
2613 opts['date'] = "%d %d" % dateutil.makedate()
2613 opts['date'] = "%d %d" % dateutil.makedate()
2614
2614
2615 @command("^qnew",
2615 @command("^qnew",
2616 [('e', 'edit', None, _('invoke editor on commit messages')),
2616 [('e', 'edit', None, _('invoke editor on commit messages')),
2617 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2617 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2618 ('g', 'git', None, _('use git extended diff format')),
2618 ('g', 'git', None, _('use git extended diff format')),
2619 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2619 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2620 ('u', 'user', '',
2620 ('u', 'user', '',
2621 _('add "From: <USER>" to patch'), _('USER')),
2621 _('add "From: <USER>" to patch'), _('USER')),
2622 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2622 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2623 ('d', 'date', '',
2623 ('d', 'date', '',
2624 _('add "Date: <DATE>" to patch'), _('DATE'))
2624 _('add "Date: <DATE>" to patch'), _('DATE'))
2625 ] + cmdutil.walkopts + cmdutil.commitopts,
2625 ] + cmdutil.walkopts + cmdutil.commitopts,
2626 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'),
2626 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'),
2627 inferrepo=True)
2627 inferrepo=True)
2628 def new(ui, repo, patch, *args, **opts):
2628 def new(ui, repo, patch, *args, **opts):
2629 """create a new patch
2629 """create a new patch
2630
2630
2631 qnew creates a new patch on top of the currently-applied patch (if
2631 qnew creates a new patch on top of the currently-applied patch (if
2632 any). The patch will be initialized with any outstanding changes
2632 any). The patch will be initialized with any outstanding changes
2633 in the working directory. You may also use -I/--include,
2633 in the working directory. You may also use -I/--include,
2634 -X/--exclude, and/or a list of files after the patch name to add
2634 -X/--exclude, and/or a list of files after the patch name to add
2635 only changes to matching files to the new patch, leaving the rest
2635 only changes to matching files to the new patch, leaving the rest
2636 as uncommitted modifications.
2636 as uncommitted modifications.
2637
2637
2638 -u/--user and -d/--date can be used to set the (given) user and
2638 -u/--user and -d/--date can be used to set the (given) user and
2639 date, respectively. -U/--currentuser and -D/--currentdate set user
2639 date, respectively. -U/--currentuser and -D/--currentdate set user
2640 to current user and date to current date.
2640 to current user and date to current date.
2641
2641
2642 -e/--edit, -m/--message or -l/--logfile set the patch header as
2642 -e/--edit, -m/--message or -l/--logfile set the patch header as
2643 well as the commit message. If none is specified, the header is
2643 well as the commit message. If none is specified, the header is
2644 empty and the commit message is '[mq]: PATCH'.
2644 empty and the commit message is '[mq]: PATCH'.
2645
2645
2646 Use the -g/--git option to keep the patch in the git extended diff
2646 Use the -g/--git option to keep the patch in the git extended diff
2647 format. Read the diffs help topic for more information on why this
2647 format. Read the diffs help topic for more information on why this
2648 is important for preserving permission changes and copy/rename
2648 is important for preserving permission changes and copy/rename
2649 information.
2649 information.
2650
2650
2651 Returns 0 on successful creation of a new patch.
2651 Returns 0 on successful creation of a new patch.
2652 """
2652 """
2653 opts = pycompat.byteskwargs(opts)
2653 opts = pycompat.byteskwargs(opts)
2654 msg = cmdutil.logmessage(ui, opts)
2654 msg = cmdutil.logmessage(ui, opts)
2655 q = repo.mq
2655 q = repo.mq
2656 opts['msg'] = msg
2656 opts['msg'] = msg
2657 setupheaderopts(ui, opts)
2657 setupheaderopts(ui, opts)
2658 q.new(repo, patch, *args, **pycompat.strkwargs(opts))
2658 q.new(repo, patch, *args, **pycompat.strkwargs(opts))
2659 q.savedirty()
2659 q.savedirty()
2660 return 0
2660 return 0
2661
2661
2662 @command("^qrefresh",
2662 @command("^qrefresh",
2663 [('e', 'edit', None, _('invoke editor on commit messages')),
2663 [('e', 'edit', None, _('invoke editor on commit messages')),
2664 ('g', 'git', None, _('use git extended diff format')),
2664 ('g', 'git', None, _('use git extended diff format')),
2665 ('s', 'short', None,
2665 ('s', 'short', None,
2666 _('refresh only files already in the patch and specified files')),
2666 _('refresh only files already in the patch and specified files')),
2667 ('U', 'currentuser', None,
2667 ('U', 'currentuser', None,
2668 _('add/update author field in patch with current user')),
2668 _('add/update author field in patch with current user')),
2669 ('u', 'user', '',
2669 ('u', 'user', '',
2670 _('add/update author field in patch with given user'), _('USER')),
2670 _('add/update author field in patch with given user'), _('USER')),
2671 ('D', 'currentdate', None,
2671 ('D', 'currentdate', None,
2672 _('add/update date field in patch with current date')),
2672 _('add/update date field in patch with current date')),
2673 ('d', 'date', '',
2673 ('d', 'date', '',
2674 _('add/update date field in patch with given date'), _('DATE'))
2674 _('add/update date field in patch with given date'), _('DATE'))
2675 ] + cmdutil.walkopts + cmdutil.commitopts,
2675 ] + cmdutil.walkopts + cmdutil.commitopts,
2676 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'),
2676 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'),
2677 inferrepo=True)
2677 inferrepo=True)
2678 def refresh(ui, repo, *pats, **opts):
2678 def refresh(ui, repo, *pats, **opts):
2679 """update the current patch
2679 """update the current patch
2680
2680
2681 If any file patterns are provided, the refreshed patch will
2681 If any file patterns are provided, the refreshed patch will
2682 contain only the modifications that match those patterns; the
2682 contain only the modifications that match those patterns; the
2683 remaining modifications will remain in the working directory.
2683 remaining modifications will remain in the working directory.
2684
2684
2685 If -s/--short is specified, files currently included in the patch
2685 If -s/--short is specified, files currently included in the patch
2686 will be refreshed just like matched files and remain in the patch.
2686 will be refreshed just like matched files and remain in the patch.
2687
2687
2688 If -e/--edit is specified, Mercurial will start your configured editor for
2688 If -e/--edit is specified, Mercurial will start your configured editor for
2689 you to enter a message. In case qrefresh fails, you will find a backup of
2689 you to enter a message. In case qrefresh fails, you will find a backup of
2690 your message in ``.hg/last-message.txt``.
2690 your message in ``.hg/last-message.txt``.
2691
2691
2692 hg add/remove/copy/rename work as usual, though you might want to
2692 hg add/remove/copy/rename work as usual, though you might want to
2693 use git-style patches (-g/--git or [diff] git=1) to track copies
2693 use git-style patches (-g/--git or [diff] git=1) to track copies
2694 and renames. See the diffs help topic for more information on the
2694 and renames. See the diffs help topic for more information on the
2695 git diff format.
2695 git diff format.
2696
2696
2697 Returns 0 on success.
2697 Returns 0 on success.
2698 """
2698 """
2699 opts = pycompat.byteskwargs(opts)
2699 opts = pycompat.byteskwargs(opts)
2700 q = repo.mq
2700 q = repo.mq
2701 message = cmdutil.logmessage(ui, opts)
2701 message = cmdutil.logmessage(ui, opts)
2702 setupheaderopts(ui, opts)
2702 setupheaderopts(ui, opts)
2703 with repo.wlock():
2703 with repo.wlock():
2704 ret = q.refresh(repo, pats, msg=message, **pycompat.strkwargs(opts))
2704 ret = q.refresh(repo, pats, msg=message, **pycompat.strkwargs(opts))
2705 q.savedirty()
2705 q.savedirty()
2706 return ret
2706 return ret
2707
2707
2708 @command("^qdiff",
2708 @command("^qdiff",
2709 cmdutil.diffopts + cmdutil.diffopts2 + cmdutil.walkopts,
2709 cmdutil.diffopts + cmdutil.diffopts2 + cmdutil.walkopts,
2710 _('hg qdiff [OPTION]... [FILE]...'),
2710 _('hg qdiff [OPTION]... [FILE]...'),
2711 inferrepo=True)
2711 inferrepo=True)
2712 def diff(ui, repo, *pats, **opts):
2712 def diff(ui, repo, *pats, **opts):
2713 """diff of the current patch and subsequent modifications
2713 """diff of the current patch and subsequent modifications
2714
2714
2715 Shows a diff which includes the current patch as well as any
2715 Shows a diff which includes the current patch as well as any
2716 changes which have been made in the working directory since the
2716 changes which have been made in the working directory since the
2717 last refresh (thus showing what the current patch would become
2717 last refresh (thus showing what the current patch would become
2718 after a qrefresh).
2718 after a qrefresh).
2719
2719
2720 Use :hg:`diff` if you only want to see the changes made since the
2720 Use :hg:`diff` if you only want to see the changes made since the
2721 last qrefresh, or :hg:`export qtip` if you want to see changes
2721 last qrefresh, or :hg:`export qtip` if you want to see changes
2722 made by the current patch without including changes made since the
2722 made by the current patch without including changes made since the
2723 qrefresh.
2723 qrefresh.
2724
2724
2725 Returns 0 on success.
2725 Returns 0 on success.
2726 """
2726 """
2727 ui.pager('qdiff')
2727 ui.pager('qdiff')
2728 repo.mq.diff(repo, pats, pycompat.byteskwargs(opts))
2728 repo.mq.diff(repo, pats, pycompat.byteskwargs(opts))
2729 return 0
2729 return 0
2730
2730
2731 @command('qfold',
2731 @command('qfold',
2732 [('e', 'edit', None, _('invoke editor on commit messages')),
2732 [('e', 'edit', None, _('invoke editor on commit messages')),
2733 ('k', 'keep', None, _('keep folded patch files')),
2733 ('k', 'keep', None, _('keep folded patch files')),
2734 ] + cmdutil.commitopts,
2734 ] + cmdutil.commitopts,
2735 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2735 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2736 def fold(ui, repo, *files, **opts):
2736 def fold(ui, repo, *files, **opts):
2737 """fold the named patches into the current patch
2737 """fold the named patches into the current patch
2738
2738
2739 Patches must not yet be applied. Each patch will be successively
2739 Patches must not yet be applied. Each patch will be successively
2740 applied to the current patch in the order given. If all the
2740 applied to the current patch in the order given. If all the
2741 patches apply successfully, the current patch will be refreshed
2741 patches apply successfully, the current patch will be refreshed
2742 with the new cumulative patch, and the folded patches will be
2742 with the new cumulative patch, and the folded patches will be
2743 deleted. With -k/--keep, the folded patch files will not be
2743 deleted. With -k/--keep, the folded patch files will not be
2744 removed afterwards.
2744 removed afterwards.
2745
2745
2746 The header for each folded patch will be concatenated with the
2746 The header for each folded patch will be concatenated with the
2747 current patch header, separated by a line of ``* * *``.
2747 current patch header, separated by a line of ``* * *``.
2748
2748
2749 Returns 0 on success."""
2749 Returns 0 on success."""
2750 opts = pycompat.byteskwargs(opts)
2750 opts = pycompat.byteskwargs(opts)
2751 q = repo.mq
2751 q = repo.mq
2752 if not files:
2752 if not files:
2753 raise error.Abort(_('qfold requires at least one patch name'))
2753 raise error.Abort(_('qfold requires at least one patch name'))
2754 if not q.checktoppatch(repo)[0]:
2754 if not q.checktoppatch(repo)[0]:
2755 raise error.Abort(_('no patches applied'))
2755 raise error.Abort(_('no patches applied'))
2756 q.checklocalchanges(repo)
2756 q.checklocalchanges(repo)
2757
2757
2758 message = cmdutil.logmessage(ui, opts)
2758 message = cmdutil.logmessage(ui, opts)
2759
2759
2760 parent = q.lookup('qtip')
2760 parent = q.lookup('qtip')
2761 patches = []
2761 patches = []
2762 messages = []
2762 messages = []
2763 for f in files:
2763 for f in files:
2764 p = q.lookup(f)
2764 p = q.lookup(f)
2765 if p in patches or p == parent:
2765 if p in patches or p == parent:
2766 ui.warn(_('skipping already folded patch %s\n') % p)
2766 ui.warn(_('skipping already folded patch %s\n') % p)
2767 if q.isapplied(p):
2767 if q.isapplied(p):
2768 raise error.Abort(_('qfold cannot fold already applied patch %s')
2768 raise error.Abort(_('qfold cannot fold already applied patch %s')
2769 % p)
2769 % p)
2770 patches.append(p)
2770 patches.append(p)
2771
2771
2772 for p in patches:
2772 for p in patches:
2773 if not message:
2773 if not message:
2774 ph = patchheader(q.join(p), q.plainmode)
2774 ph = patchheader(q.join(p), q.plainmode)
2775 if ph.message:
2775 if ph.message:
2776 messages.append(ph.message)
2776 messages.append(ph.message)
2777 pf = q.join(p)
2777 pf = q.join(p)
2778 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2778 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2779 if not patchsuccess:
2779 if not patchsuccess:
2780 raise error.Abort(_('error folding patch %s') % p)
2780 raise error.Abort(_('error folding patch %s') % p)
2781
2781
2782 if not message:
2782 if not message:
2783 ph = patchheader(q.join(parent), q.plainmode)
2783 ph = patchheader(q.join(parent), q.plainmode)
2784 message = ph.message
2784 message = ph.message
2785 for msg in messages:
2785 for msg in messages:
2786 if msg:
2786 if msg:
2787 if message:
2787 if message:
2788 message.append('* * *')
2788 message.append('* * *')
2789 message.extend(msg)
2789 message.extend(msg)
2790 message = '\n'.join(message)
2790 message = '\n'.join(message)
2791
2791
2792 diffopts = q.patchopts(q.diffopts(), *patches)
2792 diffopts = q.patchopts(q.diffopts(), *patches)
2793 with repo.wlock():
2793 with repo.wlock():
2794 q.refresh(repo, msg=message, git=diffopts.git, edit=opts.get('edit'),
2794 q.refresh(repo, msg=message, git=diffopts.git, edit=opts.get('edit'),
2795 editform='mq.qfold')
2795 editform='mq.qfold')
2796 q.delete(repo, patches, opts)
2796 q.delete(repo, patches, opts)
2797 q.savedirty()
2797 q.savedirty()
2798
2798
2799 @command("qgoto",
2799 @command("qgoto",
2800 [('', 'keep-changes', None,
2800 [('', 'keep-changes', None,
2801 _('tolerate non-conflicting local changes')),
2801 _('tolerate non-conflicting local changes')),
2802 ('f', 'force', None, _('overwrite any local changes')),
2802 ('f', 'force', None, _('overwrite any local changes')),
2803 ('', 'no-backup', None, _('do not save backup copies of files'))],
2803 ('', 'no-backup', None, _('do not save backup copies of files'))],
2804 _('hg qgoto [OPTION]... PATCH'))
2804 _('hg qgoto [OPTION]... PATCH'))
2805 def goto(ui, repo, patch, **opts):
2805 def goto(ui, repo, patch, **opts):
2806 '''push or pop patches until named patch is at top of stack
2806 '''push or pop patches until named patch is at top of stack
2807
2807
2808 Returns 0 on success.'''
2808 Returns 0 on success.'''
2809 opts = pycompat.byteskwargs(opts)
2809 opts = pycompat.byteskwargs(opts)
2810 opts = fixkeepchangesopts(ui, opts)
2810 opts = fixkeepchangesopts(ui, opts)
2811 q = repo.mq
2811 q = repo.mq
2812 patch = q.lookup(patch)
2812 patch = q.lookup(patch)
2813 nobackup = opts.get('no_backup')
2813 nobackup = opts.get('no_backup')
2814 keepchanges = opts.get('keep_changes')
2814 keepchanges = opts.get('keep_changes')
2815 if q.isapplied(patch):
2815 if q.isapplied(patch):
2816 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2816 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2817 keepchanges=keepchanges)
2817 keepchanges=keepchanges)
2818 else:
2818 else:
2819 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2819 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2820 keepchanges=keepchanges)
2820 keepchanges=keepchanges)
2821 q.savedirty()
2821 q.savedirty()
2822 return ret
2822 return ret
2823
2823
2824 @command("qguard",
2824 @command("qguard",
2825 [('l', 'list', None, _('list all patches and guards')),
2825 [('l', 'list', None, _('list all patches and guards')),
2826 ('n', 'none', None, _('drop all guards'))],
2826 ('n', 'none', None, _('drop all guards'))],
2827 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2827 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2828 def guard(ui, repo, *args, **opts):
2828 def guard(ui, repo, *args, **opts):
2829 '''set or print guards for a patch
2829 '''set or print guards for a patch
2830
2830
2831 Guards control whether a patch can be pushed. A patch with no
2831 Guards control whether a patch can be pushed. A patch with no
2832 guards is always pushed. A patch with a positive guard ("+foo") is
2832 guards is always pushed. A patch with a positive guard ("+foo") is
2833 pushed only if the :hg:`qselect` command has activated it. A patch with
2833 pushed only if the :hg:`qselect` command has activated it. A patch with
2834 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2834 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2835 has activated it.
2835 has activated it.
2836
2836
2837 With no arguments, print the currently active guards.
2837 With no arguments, print the currently active guards.
2838 With arguments, set guards for the named patch.
2838 With arguments, set guards for the named patch.
2839
2839
2840 .. note::
2840 .. note::
2841
2841
2842 Specifying negative guards now requires '--'.
2842 Specifying negative guards now requires '--'.
2843
2843
2844 To set guards on another patch::
2844 To set guards on another patch::
2845
2845
2846 hg qguard other.patch -- +2.6.17 -stable
2846 hg qguard other.patch -- +2.6.17 -stable
2847
2847
2848 Returns 0 on success.
2848 Returns 0 on success.
2849 '''
2849 '''
2850 def status(idx):
2850 def status(idx):
2851 guards = q.seriesguards[idx] or ['unguarded']
2851 guards = q.seriesguards[idx] or ['unguarded']
2852 if q.series[idx] in applied:
2852 if q.series[idx] in applied:
2853 state = 'applied'
2853 state = 'applied'
2854 elif q.pushable(idx)[0]:
2854 elif q.pushable(idx)[0]:
2855 state = 'unapplied'
2855 state = 'unapplied'
2856 else:
2856 else:
2857 state = 'guarded'
2857 state = 'guarded'
2858 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2858 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2859 ui.write('%s: ' % ui.label(q.series[idx], label))
2859 ui.write('%s: ' % ui.label(q.series[idx], label))
2860
2860
2861 for i, guard in enumerate(guards):
2861 for i, guard in enumerate(guards):
2862 if guard.startswith('+'):
2862 if guard.startswith('+'):
2863 ui.write(guard, label='qguard.positive')
2863 ui.write(guard, label='qguard.positive')
2864 elif guard.startswith('-'):
2864 elif guard.startswith('-'):
2865 ui.write(guard, label='qguard.negative')
2865 ui.write(guard, label='qguard.negative')
2866 else:
2866 else:
2867 ui.write(guard, label='qguard.unguarded')
2867 ui.write(guard, label='qguard.unguarded')
2868 if i != len(guards) - 1:
2868 if i != len(guards) - 1:
2869 ui.write(' ')
2869 ui.write(' ')
2870 ui.write('\n')
2870 ui.write('\n')
2871 q = repo.mq
2871 q = repo.mq
2872 applied = set(p.name for p in q.applied)
2872 applied = set(p.name for p in q.applied)
2873 patch = None
2873 patch = None
2874 args = list(args)
2874 args = list(args)
2875 if opts.get(r'list'):
2875 if opts.get(r'list'):
2876 if args or opts.get('none'):
2876 if args or opts.get('none'):
2877 raise error.Abort(_('cannot mix -l/--list with options or '
2877 raise error.Abort(_('cannot mix -l/--list with options or '
2878 'arguments'))
2878 'arguments'))
2879 for i in xrange(len(q.series)):
2879 for i in xrange(len(q.series)):
2880 status(i)
2880 status(i)
2881 return
2881 return
2882 if not args or args[0][0:1] in '-+':
2882 if not args or args[0][0:1] in '-+':
2883 if not q.applied:
2883 if not q.applied:
2884 raise error.Abort(_('no patches applied'))
2884 raise error.Abort(_('no patches applied'))
2885 patch = q.applied[-1].name
2885 patch = q.applied[-1].name
2886 if patch is None and args[0][0:1] not in '-+':
2886 if patch is None and args[0][0:1] not in '-+':
2887 patch = args.pop(0)
2887 patch = args.pop(0)
2888 if patch is None:
2888 if patch is None:
2889 raise error.Abort(_('no patch to work with'))
2889 raise error.Abort(_('no patch to work with'))
2890 if args or opts.get('none'):
2890 if args or opts.get('none'):
2891 idx = q.findseries(patch)
2891 idx = q.findseries(patch)
2892 if idx is None:
2892 if idx is None:
2893 raise error.Abort(_('no patch named %s') % patch)
2893 raise error.Abort(_('no patch named %s') % patch)
2894 q.setguards(idx, args)
2894 q.setguards(idx, args)
2895 q.savedirty()
2895 q.savedirty()
2896 else:
2896 else:
2897 status(q.series.index(q.lookup(patch)))
2897 status(q.series.index(q.lookup(patch)))
2898
2898
2899 @command("qheader", [], _('hg qheader [PATCH]'))
2899 @command("qheader", [], _('hg qheader [PATCH]'))
2900 def header(ui, repo, patch=None):
2900 def header(ui, repo, patch=None):
2901 """print the header of the topmost or specified patch
2901 """print the header of the topmost or specified patch
2902
2902
2903 Returns 0 on success."""
2903 Returns 0 on success."""
2904 q = repo.mq
2904 q = repo.mq
2905
2905
2906 if patch:
2906 if patch:
2907 patch = q.lookup(patch)
2907 patch = q.lookup(patch)
2908 else:
2908 else:
2909 if not q.applied:
2909 if not q.applied:
2910 ui.write(_('no patches applied\n'))
2910 ui.write(_('no patches applied\n'))
2911 return 1
2911 return 1
2912 patch = q.lookup('qtip')
2912 patch = q.lookup('qtip')
2913 ph = patchheader(q.join(patch), q.plainmode)
2913 ph = patchheader(q.join(patch), q.plainmode)
2914
2914
2915 ui.write('\n'.join(ph.message) + '\n')
2915 ui.write('\n'.join(ph.message) + '\n')
2916
2916
2917 def lastsavename(path):
2917 def lastsavename(path):
2918 (directory, base) = os.path.split(path)
2918 (directory, base) = os.path.split(path)
2919 names = os.listdir(directory)
2919 names = os.listdir(directory)
2920 namere = re.compile("%s.([0-9]+)" % base)
2920 namere = re.compile("%s.([0-9]+)" % base)
2921 maxindex = None
2921 maxindex = None
2922 maxname = None
2922 maxname = None
2923 for f in names:
2923 for f in names:
2924 m = namere.match(f)
2924 m = namere.match(f)
2925 if m:
2925 if m:
2926 index = int(m.group(1))
2926 index = int(m.group(1))
2927 if maxindex is None or index > maxindex:
2927 if maxindex is None or index > maxindex:
2928 maxindex = index
2928 maxindex = index
2929 maxname = f
2929 maxname = f
2930 if maxname:
2930 if maxname:
2931 return (os.path.join(directory, maxname), maxindex)
2931 return (os.path.join(directory, maxname), maxindex)
2932 return (None, None)
2932 return (None, None)
2933
2933
2934 def savename(path):
2934 def savename(path):
2935 (last, index) = lastsavename(path)
2935 (last, index) = lastsavename(path)
2936 if last is None:
2936 if last is None:
2937 index = 0
2937 index = 0
2938 newpath = path + ".%d" % (index + 1)
2938 newpath = path + ".%d" % (index + 1)
2939 return newpath
2939 return newpath
2940
2940
2941 @command("^qpush",
2941 @command("^qpush",
2942 [('', 'keep-changes', None,
2942 [('', 'keep-changes', None,
2943 _('tolerate non-conflicting local changes')),
2943 _('tolerate non-conflicting local changes')),
2944 ('f', 'force', None, _('apply on top of local changes')),
2944 ('f', 'force', None, _('apply on top of local changes')),
2945 ('e', 'exact', None,
2945 ('e', 'exact', None,
2946 _('apply the target patch to its recorded parent')),
2946 _('apply the target patch to its recorded parent')),
2947 ('l', 'list', None, _('list patch name in commit text')),
2947 ('l', 'list', None, _('list patch name in commit text')),
2948 ('a', 'all', None, _('apply all patches')),
2948 ('a', 'all', None, _('apply all patches')),
2949 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2949 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2950 ('n', 'name', '',
2950 ('n', 'name', '',
2951 _('merge queue name (DEPRECATED)'), _('NAME')),
2951 _('merge queue name (DEPRECATED)'), _('NAME')),
2952 ('', 'move', None,
2952 ('', 'move', None,
2953 _('reorder patch series and apply only the patch')),
2953 _('reorder patch series and apply only the patch')),
2954 ('', 'no-backup', None, _('do not save backup copies of files'))],
2954 ('', 'no-backup', None, _('do not save backup copies of files'))],
2955 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2955 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2956 def push(ui, repo, patch=None, **opts):
2956 def push(ui, repo, patch=None, **opts):
2957 """push the next patch onto the stack
2957 """push the next patch onto the stack
2958
2958
2959 By default, abort if the working directory contains uncommitted
2959 By default, abort if the working directory contains uncommitted
2960 changes. With --keep-changes, abort only if the uncommitted files
2960 changes. With --keep-changes, abort only if the uncommitted files
2961 overlap with patched files. With -f/--force, backup and patch over
2961 overlap with patched files. With -f/--force, backup and patch over
2962 uncommitted changes.
2962 uncommitted changes.
2963
2963
2964 Return 0 on success.
2964 Return 0 on success.
2965 """
2965 """
2966 q = repo.mq
2966 q = repo.mq
2967 mergeq = None
2967 mergeq = None
2968
2968
2969 opts = pycompat.byteskwargs(opts)
2969 opts = pycompat.byteskwargs(opts)
2970 opts = fixkeepchangesopts(ui, opts)
2970 opts = fixkeepchangesopts(ui, opts)
2971 if opts.get('merge'):
2971 if opts.get('merge'):
2972 if opts.get('name'):
2972 if opts.get('name'):
2973 newpath = repo.vfs.join(opts.get('name'))
2973 newpath = repo.vfs.join(opts.get('name'))
2974 else:
2974 else:
2975 newpath, i = lastsavename(q.path)
2975 newpath, i = lastsavename(q.path)
2976 if not newpath:
2976 if not newpath:
2977 ui.warn(_("no saved queues found, please use -n\n"))
2977 ui.warn(_("no saved queues found, please use -n\n"))
2978 return 1
2978 return 1
2979 mergeq = queue(ui, repo.baseui, repo.path, newpath)
2979 mergeq = queue(ui, repo.baseui, repo.path, newpath)
2980 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2980 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2981 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2981 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2982 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2982 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2983 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2983 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2984 keepchanges=opts.get('keep_changes'))
2984 keepchanges=opts.get('keep_changes'))
2985 return ret
2985 return ret
2986
2986
2987 @command("^qpop",
2987 @command("^qpop",
2988 [('a', 'all', None, _('pop all patches')),
2988 [('a', 'all', None, _('pop all patches')),
2989 ('n', 'name', '',
2989 ('n', 'name', '',
2990 _('queue name to pop (DEPRECATED)'), _('NAME')),
2990 _('queue name to pop (DEPRECATED)'), _('NAME')),
2991 ('', 'keep-changes', None,
2991 ('', 'keep-changes', None,
2992 _('tolerate non-conflicting local changes')),
2992 _('tolerate non-conflicting local changes')),
2993 ('f', 'force', None, _('forget any local changes to patched files')),
2993 ('f', 'force', None, _('forget any local changes to patched files')),
2994 ('', 'no-backup', None, _('do not save backup copies of files'))],
2994 ('', 'no-backup', None, _('do not save backup copies of files'))],
2995 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2995 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2996 def pop(ui, repo, patch=None, **opts):
2996 def pop(ui, repo, patch=None, **opts):
2997 """pop the current patch off the stack
2997 """pop the current patch off the stack
2998
2998
2999 Without argument, pops off the top of the patch stack. If given a
2999 Without argument, pops off the top of the patch stack. If given a
3000 patch name, keeps popping off patches until the named patch is at
3000 patch name, keeps popping off patches until the named patch is at
3001 the top of the stack.
3001 the top of the stack.
3002
3002
3003 By default, abort if the working directory contains uncommitted
3003 By default, abort if the working directory contains uncommitted
3004 changes. With --keep-changes, abort only if the uncommitted files
3004 changes. With --keep-changes, abort only if the uncommitted files
3005 overlap with patched files. With -f/--force, backup and discard
3005 overlap with patched files. With -f/--force, backup and discard
3006 changes made to such files.
3006 changes made to such files.
3007
3007
3008 Return 0 on success.
3008 Return 0 on success.
3009 """
3009 """
3010 opts = pycompat.byteskwargs(opts)
3010 opts = pycompat.byteskwargs(opts)
3011 opts = fixkeepchangesopts(ui, opts)
3011 opts = fixkeepchangesopts(ui, opts)
3012 localupdate = True
3012 localupdate = True
3013 if opts.get('name'):
3013 if opts.get('name'):
3014 q = queue(ui, repo.baseui, repo.path, repo.vfs.join(opts.get('name')))
3014 q = queue(ui, repo.baseui, repo.path, repo.vfs.join(opts.get('name')))
3015 ui.warn(_('using patch queue: %s\n') % q.path)
3015 ui.warn(_('using patch queue: %s\n') % q.path)
3016 localupdate = False
3016 localupdate = False
3017 else:
3017 else:
3018 q = repo.mq
3018 q = repo.mq
3019 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
3019 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
3020 all=opts.get('all'), nobackup=opts.get('no_backup'),
3020 all=opts.get('all'), nobackup=opts.get('no_backup'),
3021 keepchanges=opts.get('keep_changes'))
3021 keepchanges=opts.get('keep_changes'))
3022 q.savedirty()
3022 q.savedirty()
3023 return ret
3023 return ret
3024
3024
3025 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
3025 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
3026 def rename(ui, repo, patch, name=None, **opts):
3026 def rename(ui, repo, patch, name=None, **opts):
3027 """rename a patch
3027 """rename a patch
3028
3028
3029 With one argument, renames the current patch to PATCH1.
3029 With one argument, renames the current patch to PATCH1.
3030 With two arguments, renames PATCH1 to PATCH2.
3030 With two arguments, renames PATCH1 to PATCH2.
3031
3031
3032 Returns 0 on success."""
3032 Returns 0 on success."""
3033 q = repo.mq
3033 q = repo.mq
3034 if not name:
3034 if not name:
3035 name = patch
3035 name = patch
3036 patch = None
3036 patch = None
3037
3037
3038 if patch:
3038 if patch:
3039 patch = q.lookup(patch)
3039 patch = q.lookup(patch)
3040 else:
3040 else:
3041 if not q.applied:
3041 if not q.applied:
3042 ui.write(_('no patches applied\n'))
3042 ui.write(_('no patches applied\n'))
3043 return
3043 return
3044 patch = q.lookup('qtip')
3044 patch = q.lookup('qtip')
3045 absdest = q.join(name)
3045 absdest = q.join(name)
3046 if os.path.isdir(absdest):
3046 if os.path.isdir(absdest):
3047 name = normname(os.path.join(name, os.path.basename(patch)))
3047 name = normname(os.path.join(name, os.path.basename(patch)))
3048 absdest = q.join(name)
3048 absdest = q.join(name)
3049 q.checkpatchname(name)
3049 q.checkpatchname(name)
3050
3050
3051 ui.note(_('renaming %s to %s\n') % (patch, name))
3051 ui.note(_('renaming %s to %s\n') % (patch, name))
3052 i = q.findseries(patch)
3052 i = q.findseries(patch)
3053 guards = q.guard_re.findall(q.fullseries[i])
3053 guards = q.guard_re.findall(q.fullseries[i])
3054 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
3054 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
3055 q.parseseries()
3055 q.parseseries()
3056 q.seriesdirty = True
3056 q.seriesdirty = True
3057
3057
3058 info = q.isapplied(patch)
3058 info = q.isapplied(patch)
3059 if info:
3059 if info:
3060 q.applied[info[0]] = statusentry(info[1], name)
3060 q.applied[info[0]] = statusentry(info[1], name)
3061 q.applieddirty = True
3061 q.applieddirty = True
3062
3062
3063 destdir = os.path.dirname(absdest)
3063 destdir = os.path.dirname(absdest)
3064 if not os.path.isdir(destdir):
3064 if not os.path.isdir(destdir):
3065 os.makedirs(destdir)
3065 os.makedirs(destdir)
3066 util.rename(q.join(patch), absdest)
3066 util.rename(q.join(patch), absdest)
3067 r = q.qrepo()
3067 r = q.qrepo()
3068 if r and patch in r.dirstate:
3068 if r and patch in r.dirstate:
3069 wctx = r[None]
3069 wctx = r[None]
3070 with r.wlock():
3070 with r.wlock():
3071 if r.dirstate[patch] == 'a':
3071 if r.dirstate[patch] == 'a':
3072 r.dirstate.drop(patch)
3072 r.dirstate.drop(patch)
3073 r.dirstate.add(name)
3073 r.dirstate.add(name)
3074 else:
3074 else:
3075 wctx.copy(patch, name)
3075 wctx.copy(patch, name)
3076 wctx.forget([patch])
3076 wctx.forget([patch])
3077
3077
3078 q.savedirty()
3078 q.savedirty()
3079
3079
3080 @command("qrestore",
3080 @command("qrestore",
3081 [('d', 'delete', None, _('delete save entry')),
3081 [('d', 'delete', None, _('delete save entry')),
3082 ('u', 'update', None, _('update queue working directory'))],
3082 ('u', 'update', None, _('update queue working directory'))],
3083 _('hg qrestore [-d] [-u] REV'))
3083 _('hg qrestore [-d] [-u] REV'))
3084 def restore(ui, repo, rev, **opts):
3084 def restore(ui, repo, rev, **opts):
3085 """restore the queue state saved by a revision (DEPRECATED)
3085 """restore the queue state saved by a revision (DEPRECATED)
3086
3086
3087 This command is deprecated, use :hg:`rebase` instead."""
3087 This command is deprecated, use :hg:`rebase` instead."""
3088 rev = repo.lookup(rev)
3088 rev = repo.lookup(rev)
3089 q = repo.mq
3089 q = repo.mq
3090 q.restore(repo, rev, delete=opts.get(r'delete'),
3090 q.restore(repo, rev, delete=opts.get(r'delete'),
3091 qupdate=opts.get(r'update'))
3091 qupdate=opts.get(r'update'))
3092 q.savedirty()
3092 q.savedirty()
3093 return 0
3093 return 0
3094
3094
3095 @command("qsave",
3095 @command("qsave",
3096 [('c', 'copy', None, _('copy patch directory')),
3096 [('c', 'copy', None, _('copy patch directory')),
3097 ('n', 'name', '',
3097 ('n', 'name', '',
3098 _('copy directory name'), _('NAME')),
3098 _('copy directory name'), _('NAME')),
3099 ('e', 'empty', None, _('clear queue status file')),
3099 ('e', 'empty', None, _('clear queue status file')),
3100 ('f', 'force', None, _('force copy'))] + cmdutil.commitopts,
3100 ('f', 'force', None, _('force copy'))] + cmdutil.commitopts,
3101 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
3101 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
3102 def save(ui, repo, **opts):
3102 def save(ui, repo, **opts):
3103 """save current queue state (DEPRECATED)
3103 """save current queue state (DEPRECATED)
3104
3104
3105 This command is deprecated, use :hg:`rebase` instead."""
3105 This command is deprecated, use :hg:`rebase` instead."""
3106 q = repo.mq
3106 q = repo.mq
3107 opts = pycompat.byteskwargs(opts)
3107 opts = pycompat.byteskwargs(opts)
3108 message = cmdutil.logmessage(ui, opts)
3108 message = cmdutil.logmessage(ui, opts)
3109 ret = q.save(repo, msg=message)
3109 ret = q.save(repo, msg=message)
3110 if ret:
3110 if ret:
3111 return ret
3111 return ret
3112 q.savedirty() # save to .hg/patches before copying
3112 q.savedirty() # save to .hg/patches before copying
3113 if opts.get('copy'):
3113 if opts.get('copy'):
3114 path = q.path
3114 path = q.path
3115 if opts.get('name'):
3115 if opts.get('name'):
3116 newpath = os.path.join(q.basepath, opts.get('name'))
3116 newpath = os.path.join(q.basepath, opts.get('name'))
3117 if os.path.exists(newpath):
3117 if os.path.exists(newpath):
3118 if not os.path.isdir(newpath):
3118 if not os.path.isdir(newpath):
3119 raise error.Abort(_('destination %s exists and is not '
3119 raise error.Abort(_('destination %s exists and is not '
3120 'a directory') % newpath)
3120 'a directory') % newpath)
3121 if not opts.get('force'):
3121 if not opts.get('force'):
3122 raise error.Abort(_('destination %s exists, '
3122 raise error.Abort(_('destination %s exists, '
3123 'use -f to force') % newpath)
3123 'use -f to force') % newpath)
3124 else:
3124 else:
3125 newpath = savename(path)
3125 newpath = savename(path)
3126 ui.warn(_("copy %s to %s\n") % (path, newpath))
3126 ui.warn(_("copy %s to %s\n") % (path, newpath))
3127 util.copyfiles(path, newpath)
3127 util.copyfiles(path, newpath)
3128 if opts.get('empty'):
3128 if opts.get('empty'):
3129 del q.applied[:]
3129 del q.applied[:]
3130 q.applieddirty = True
3130 q.applieddirty = True
3131 q.savedirty()
3131 q.savedirty()
3132 return 0
3132 return 0
3133
3133
3134
3134
3135 @command("qselect",
3135 @command("qselect",
3136 [('n', 'none', None, _('disable all guards')),
3136 [('n', 'none', None, _('disable all guards')),
3137 ('s', 'series', None, _('list all guards in series file')),
3137 ('s', 'series', None, _('list all guards in series file')),
3138 ('', 'pop', None, _('pop to before first guarded applied patch')),
3138 ('', 'pop', None, _('pop to before first guarded applied patch')),
3139 ('', 'reapply', None, _('pop, then reapply patches'))],
3139 ('', 'reapply', None, _('pop, then reapply patches'))],
3140 _('hg qselect [OPTION]... [GUARD]...'))
3140 _('hg qselect [OPTION]... [GUARD]...'))
3141 def select(ui, repo, *args, **opts):
3141 def select(ui, repo, *args, **opts):
3142 '''set or print guarded patches to push
3142 '''set or print guarded patches to push
3143
3143
3144 Use the :hg:`qguard` command to set or print guards on patch, then use
3144 Use the :hg:`qguard` command to set or print guards on patch, then use
3145 qselect to tell mq which guards to use. A patch will be pushed if
3145 qselect to tell mq which guards to use. A patch will be pushed if
3146 it has no guards or any positive guards match the currently
3146 it has no guards or any positive guards match the currently
3147 selected guard, but will not be pushed if any negative guards
3147 selected guard, but will not be pushed if any negative guards
3148 match the current guard. For example::
3148 match the current guard. For example::
3149
3149
3150 qguard foo.patch -- -stable (negative guard)
3150 qguard foo.patch -- -stable (negative guard)
3151 qguard bar.patch +stable (positive guard)
3151 qguard bar.patch +stable (positive guard)
3152 qselect stable
3152 qselect stable
3153
3153
3154 This activates the "stable" guard. mq will skip foo.patch (because
3154 This activates the "stable" guard. mq will skip foo.patch (because
3155 it has a negative match) but push bar.patch (because it has a
3155 it has a negative match) but push bar.patch (because it has a
3156 positive match).
3156 positive match).
3157
3157
3158 With no arguments, prints the currently active guards.
3158 With no arguments, prints the currently active guards.
3159 With one argument, sets the active guard.
3159 With one argument, sets the active guard.
3160
3160
3161 Use -n/--none to deactivate guards (no other arguments needed).
3161 Use -n/--none to deactivate guards (no other arguments needed).
3162 When no guards are active, patches with positive guards are
3162 When no guards are active, patches with positive guards are
3163 skipped and patches with negative guards are pushed.
3163 skipped and patches with negative guards are pushed.
3164
3164
3165 qselect can change the guards on applied patches. It does not pop
3165 qselect can change the guards on applied patches. It does not pop
3166 guarded patches by default. Use --pop to pop back to the last
3166 guarded patches by default. Use --pop to pop back to the last
3167 applied patch that is not guarded. Use --reapply (which implies
3167 applied patch that is not guarded. Use --reapply (which implies
3168 --pop) to push back to the current patch afterwards, but skip
3168 --pop) to push back to the current patch afterwards, but skip
3169 guarded patches.
3169 guarded patches.
3170
3170
3171 Use -s/--series to print a list of all guards in the series file
3171 Use -s/--series to print a list of all guards in the series file
3172 (no other arguments needed). Use -v for more information.
3172 (no other arguments needed). Use -v for more information.
3173
3173
3174 Returns 0 on success.'''
3174 Returns 0 on success.'''
3175
3175
3176 q = repo.mq
3176 q = repo.mq
3177 opts = pycompat.byteskwargs(opts)
3177 opts = pycompat.byteskwargs(opts)
3178 guards = q.active()
3178 guards = q.active()
3179 pushable = lambda i: q.pushable(q.applied[i].name)[0]
3179 pushable = lambda i: q.pushable(q.applied[i].name)[0]
3180 if args or opts.get('none'):
3180 if args or opts.get('none'):
3181 old_unapplied = q.unapplied(repo)
3181 old_unapplied = q.unapplied(repo)
3182 old_guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
3182 old_guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
3183 q.setactive(args)
3183 q.setactive(args)
3184 q.savedirty()
3184 q.savedirty()
3185 if not args:
3185 if not args:
3186 ui.status(_('guards deactivated\n'))
3186 ui.status(_('guards deactivated\n'))
3187 if not opts.get('pop') and not opts.get('reapply'):
3187 if not opts.get('pop') and not opts.get('reapply'):
3188 unapplied = q.unapplied(repo)
3188 unapplied = q.unapplied(repo)
3189 guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
3189 guarded = [i for i in xrange(len(q.applied)) if not pushable(i)]
3190 if len(unapplied) != len(old_unapplied):
3190 if len(unapplied) != len(old_unapplied):
3191 ui.status(_('number of unguarded, unapplied patches has '
3191 ui.status(_('number of unguarded, unapplied patches has '
3192 'changed from %d to %d\n') %
3192 'changed from %d to %d\n') %
3193 (len(old_unapplied), len(unapplied)))
3193 (len(old_unapplied), len(unapplied)))
3194 if len(guarded) != len(old_guarded):
3194 if len(guarded) != len(old_guarded):
3195 ui.status(_('number of guarded, applied patches has changed '
3195 ui.status(_('number of guarded, applied patches has changed '
3196 'from %d to %d\n') %
3196 'from %d to %d\n') %
3197 (len(old_guarded), len(guarded)))
3197 (len(old_guarded), len(guarded)))
3198 elif opts.get('series'):
3198 elif opts.get('series'):
3199 guards = {}
3199 guards = {}
3200 noguards = 0
3200 noguards = 0
3201 for gs in q.seriesguards:
3201 for gs in q.seriesguards:
3202 if not gs:
3202 if not gs:
3203 noguards += 1
3203 noguards += 1
3204 for g in gs:
3204 for g in gs:
3205 guards.setdefault(g, 0)
3205 guards.setdefault(g, 0)
3206 guards[g] += 1
3206 guards[g] += 1
3207 if ui.verbose:
3207 if ui.verbose:
3208 guards['NONE'] = noguards
3208 guards['NONE'] = noguards
3209 guards = list(guards.items())
3209 guards = list(guards.items())
3210 guards.sort(key=lambda x: x[0][1:])
3210 guards.sort(key=lambda x: x[0][1:])
3211 if guards:
3211 if guards:
3212 ui.note(_('guards in series file:\n'))
3212 ui.note(_('guards in series file:\n'))
3213 for guard, count in guards:
3213 for guard, count in guards:
3214 ui.note('%2d ' % count)
3214 ui.note('%2d ' % count)
3215 ui.write(guard, '\n')
3215 ui.write(guard, '\n')
3216 else:
3216 else:
3217 ui.note(_('no guards in series file\n'))
3217 ui.note(_('no guards in series file\n'))
3218 else:
3218 else:
3219 if guards:
3219 if guards:
3220 ui.note(_('active guards:\n'))
3220 ui.note(_('active guards:\n'))
3221 for g in guards:
3221 for g in guards:
3222 ui.write(g, '\n')
3222 ui.write(g, '\n')
3223 else:
3223 else:
3224 ui.write(_('no active guards\n'))
3224 ui.write(_('no active guards\n'))
3225 reapply = opts.get('reapply') and q.applied and q.applied[-1].name
3225 reapply = opts.get('reapply') and q.applied and q.applied[-1].name
3226 popped = False
3226 popped = False
3227 if opts.get('pop') or opts.get('reapply'):
3227 if opts.get('pop') or opts.get('reapply'):
3228 for i in xrange(len(q.applied)):
3228 for i in xrange(len(q.applied)):
3229 if not pushable(i):
3229 if not pushable(i):
3230 ui.status(_('popping guarded patches\n'))
3230 ui.status(_('popping guarded patches\n'))
3231 popped = True
3231 popped = True
3232 if i == 0:
3232 if i == 0:
3233 q.pop(repo, all=True)
3233 q.pop(repo, all=True)
3234 else:
3234 else:
3235 q.pop(repo, q.applied[i - 1].name)
3235 q.pop(repo, q.applied[i - 1].name)
3236 break
3236 break
3237 if popped:
3237 if popped:
3238 try:
3238 try:
3239 if reapply:
3239 if reapply:
3240 ui.status(_('reapplying unguarded patches\n'))
3240 ui.status(_('reapplying unguarded patches\n'))
3241 q.push(repo, reapply)
3241 q.push(repo, reapply)
3242 finally:
3242 finally:
3243 q.savedirty()
3243 q.savedirty()
3244
3244
3245 @command("qfinish",
3245 @command("qfinish",
3246 [('a', 'applied', None, _('finish all applied changesets'))],
3246 [('a', 'applied', None, _('finish all applied changesets'))],
3247 _('hg qfinish [-a] [REV]...'))
3247 _('hg qfinish [-a] [REV]...'))
3248 def finish(ui, repo, *revrange, **opts):
3248 def finish(ui, repo, *revrange, **opts):
3249 """move applied patches into repository history
3249 """move applied patches into repository history
3250
3250
3251 Finishes the specified revisions (corresponding to applied
3251 Finishes the specified revisions (corresponding to applied
3252 patches) by moving them out of mq control into regular repository
3252 patches) by moving them out of mq control into regular repository
3253 history.
3253 history.
3254
3254
3255 Accepts a revision range or the -a/--applied option. If --applied
3255 Accepts a revision range or the -a/--applied option. If --applied
3256 is specified, all applied mq revisions are removed from mq
3256 is specified, all applied mq revisions are removed from mq
3257 control. Otherwise, the given revisions must be at the base of the
3257 control. Otherwise, the given revisions must be at the base of the
3258 stack of applied patches.
3258 stack of applied patches.
3259
3259
3260 This can be especially useful if your changes have been applied to
3260 This can be especially useful if your changes have been applied to
3261 an upstream repository, or if you are about to push your changes
3261 an upstream repository, or if you are about to push your changes
3262 to upstream.
3262 to upstream.
3263
3263
3264 Returns 0 on success.
3264 Returns 0 on success.
3265 """
3265 """
3266 if not opts.get(r'applied') and not revrange:
3266 if not opts.get(r'applied') and not revrange:
3267 raise error.Abort(_('no revisions specified'))
3267 raise error.Abort(_('no revisions specified'))
3268 elif opts.get(r'applied'):
3268 elif opts.get(r'applied'):
3269 revrange = ('qbase::qtip',) + revrange
3269 revrange = ('qbase::qtip',) + revrange
3270
3270
3271 q = repo.mq
3271 q = repo.mq
3272 if not q.applied:
3272 if not q.applied:
3273 ui.status(_('no patches applied\n'))
3273 ui.status(_('no patches applied\n'))
3274 return 0
3274 return 0
3275
3275
3276 revs = scmutil.revrange(repo, revrange)
3276 revs = scmutil.revrange(repo, revrange)
3277 if repo['.'].rev() in revs and repo[None].files():
3277 if repo['.'].rev() in revs and repo[None].files():
3278 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3278 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3279 # queue.finish may changes phases but leave the responsibility to lock the
3279 # queue.finish may changes phases but leave the responsibility to lock the
3280 # repo to the caller to avoid deadlock with wlock. This command code is
3280 # repo to the caller to avoid deadlock with wlock. This command code is
3281 # responsibility for this locking.
3281 # responsibility for this locking.
3282 with repo.lock():
3282 with repo.lock():
3283 q.finish(repo, revs)
3283 q.finish(repo, revs)
3284 q.savedirty()
3284 q.savedirty()
3285 return 0
3285 return 0
3286
3286
3287 @command("qqueue",
3287 @command("qqueue",
3288 [('l', 'list', False, _('list all available queues')),
3288 [('l', 'list', False, _('list all available queues')),
3289 ('', 'active', False, _('print name of active queue')),
3289 ('', 'active', False, _('print name of active queue')),
3290 ('c', 'create', False, _('create new queue')),
3290 ('c', 'create', False, _('create new queue')),
3291 ('', 'rename', False, _('rename active queue')),
3291 ('', 'rename', False, _('rename active queue')),
3292 ('', 'delete', False, _('delete reference to queue')),
3292 ('', 'delete', False, _('delete reference to queue')),
3293 ('', 'purge', False, _('delete queue, and remove patch dir')),
3293 ('', 'purge', False, _('delete queue, and remove patch dir')),
3294 ],
3294 ],
3295 _('[OPTION] [QUEUE]'))
3295 _('[OPTION] [QUEUE]'))
3296 def qqueue(ui, repo, name=None, **opts):
3296 def qqueue(ui, repo, name=None, **opts):
3297 '''manage multiple patch queues
3297 '''manage multiple patch queues
3298
3298
3299 Supports switching between different patch queues, as well as creating
3299 Supports switching between different patch queues, as well as creating
3300 new patch queues and deleting existing ones.
3300 new patch queues and deleting existing ones.
3301
3301
3302 Omitting a queue name or specifying -l/--list will show you the registered
3302 Omitting a queue name or specifying -l/--list will show you the registered
3303 queues - by default the "normal" patches queue is registered. The currently
3303 queues - by default the "normal" patches queue is registered. The currently
3304 active queue will be marked with "(active)". Specifying --active will print
3304 active queue will be marked with "(active)". Specifying --active will print
3305 only the name of the active queue.
3305 only the name of the active queue.
3306
3306
3307 To create a new queue, use -c/--create. The queue is automatically made
3307 To create a new queue, use -c/--create. The queue is automatically made
3308 active, except in the case where there are applied patches from the
3308 active, except in the case where there are applied patches from the
3309 currently active queue in the repository. Then the queue will only be
3309 currently active queue in the repository. Then the queue will only be
3310 created and switching will fail.
3310 created and switching will fail.
3311
3311
3312 To delete an existing queue, use --delete. You cannot delete the currently
3312 To delete an existing queue, use --delete. You cannot delete the currently
3313 active queue.
3313 active queue.
3314
3314
3315 Returns 0 on success.
3315 Returns 0 on success.
3316 '''
3316 '''
3317 q = repo.mq
3317 q = repo.mq
3318 _defaultqueue = 'patches'
3318 _defaultqueue = 'patches'
3319 _allqueues = 'patches.queues'
3319 _allqueues = 'patches.queues'
3320 _activequeue = 'patches.queue'
3320 _activequeue = 'patches.queue'
3321
3321
3322 def _getcurrent():
3322 def _getcurrent():
3323 cur = os.path.basename(q.path)
3323 cur = os.path.basename(q.path)
3324 if cur.startswith('patches-'):
3324 if cur.startswith('patches-'):
3325 cur = cur[8:]
3325 cur = cur[8:]
3326 return cur
3326 return cur
3327
3327
3328 def _noqueues():
3328 def _noqueues():
3329 try:
3329 try:
3330 fh = repo.vfs(_allqueues, 'r')
3330 fh = repo.vfs(_allqueues, 'r')
3331 fh.close()
3331 fh.close()
3332 except IOError:
3332 except IOError:
3333 return True
3333 return True
3334
3334
3335 return False
3335 return False
3336
3336
3337 def _getqueues():
3337 def _getqueues():
3338 current = _getcurrent()
3338 current = _getcurrent()
3339
3339
3340 try:
3340 try:
3341 fh = repo.vfs(_allqueues, 'r')
3341 fh = repo.vfs(_allqueues, 'r')
3342 queues = [queue.strip() for queue in fh if queue.strip()]
3342 queues = [queue.strip() for queue in fh if queue.strip()]
3343 fh.close()
3343 fh.close()
3344 if current not in queues:
3344 if current not in queues:
3345 queues.append(current)
3345 queues.append(current)
3346 except IOError:
3346 except IOError:
3347 queues = [_defaultqueue]
3347 queues = [_defaultqueue]
3348
3348
3349 return sorted(queues)
3349 return sorted(queues)
3350
3350
3351 def _setactive(name):
3351 def _setactive(name):
3352 if q.applied:
3352 if q.applied:
3353 raise error.Abort(_('new queue created, but cannot make active '
3353 raise error.Abort(_('new queue created, but cannot make active '
3354 'as patches are applied'))
3354 'as patches are applied'))
3355 _setactivenocheck(name)
3355 _setactivenocheck(name)
3356
3356
3357 def _setactivenocheck(name):
3357 def _setactivenocheck(name):
3358 fh = repo.vfs(_activequeue, 'w')
3358 fh = repo.vfs(_activequeue, 'w')
3359 if name != 'patches':
3359 if name != 'patches':
3360 fh.write(name)
3360 fh.write(name)
3361 fh.close()
3361 fh.close()
3362
3362
3363 def _addqueue(name):
3363 def _addqueue(name):
3364 fh = repo.vfs(_allqueues, 'a')
3364 fh = repo.vfs(_allqueues, 'a')
3365 fh.write('%s\n' % (name,))
3365 fh.write('%s\n' % (name,))
3366 fh.close()
3366 fh.close()
3367
3367
3368 def _queuedir(name):
3368 def _queuedir(name):
3369 if name == 'patches':
3369 if name == 'patches':
3370 return repo.vfs.join('patches')
3370 return repo.vfs.join('patches')
3371 else:
3371 else:
3372 return repo.vfs.join('patches-' + name)
3372 return repo.vfs.join('patches-' + name)
3373
3373
3374 def _validname(name):
3374 def _validname(name):
3375 for n in name:
3375 for n in name:
3376 if n in ':\\/.':
3376 if n in ':\\/.':
3377 return False
3377 return False
3378 return True
3378 return True
3379
3379
3380 def _delete(name):
3380 def _delete(name):
3381 if name not in existing:
3381 if name not in existing:
3382 raise error.Abort(_('cannot delete queue that does not exist'))
3382 raise error.Abort(_('cannot delete queue that does not exist'))
3383
3383
3384 current = _getcurrent()
3384 current = _getcurrent()
3385
3385
3386 if name == current:
3386 if name == current:
3387 raise error.Abort(_('cannot delete currently active queue'))
3387 raise error.Abort(_('cannot delete currently active queue'))
3388
3388
3389 fh = repo.vfs('patches.queues.new', 'w')
3389 fh = repo.vfs('patches.queues.new', 'w')
3390 for queue in existing:
3390 for queue in existing:
3391 if queue == name:
3391 if queue == name:
3392 continue
3392 continue
3393 fh.write('%s\n' % (queue,))
3393 fh.write('%s\n' % (queue,))
3394 fh.close()
3394 fh.close()
3395 repo.vfs.rename('patches.queues.new', _allqueues)
3395 repo.vfs.rename('patches.queues.new', _allqueues)
3396
3396
3397 opts = pycompat.byteskwargs(opts)
3397 opts = pycompat.byteskwargs(opts)
3398 if not name or opts.get('list') or opts.get('active'):
3398 if not name or opts.get('list') or opts.get('active'):
3399 current = _getcurrent()
3399 current = _getcurrent()
3400 if opts.get('active'):
3400 if opts.get('active'):
3401 ui.write('%s\n' % (current,))
3401 ui.write('%s\n' % (current,))
3402 return
3402 return
3403 for queue in _getqueues():
3403 for queue in _getqueues():
3404 ui.write('%s' % (queue,))
3404 ui.write('%s' % (queue,))
3405 if queue == current and not ui.quiet:
3405 if queue == current and not ui.quiet:
3406 ui.write(_(' (active)\n'))
3406 ui.write(_(' (active)\n'))
3407 else:
3407 else:
3408 ui.write('\n')
3408 ui.write('\n')
3409 return
3409 return
3410
3410
3411 if not _validname(name):
3411 if not _validname(name):
3412 raise error.Abort(
3412 raise error.Abort(
3413 _('invalid queue name, may not contain the characters ":\\/."'))
3413 _('invalid queue name, may not contain the characters ":\\/."'))
3414
3414
3415 with repo.wlock():
3415 with repo.wlock():
3416 existing = _getqueues()
3416 existing = _getqueues()
3417
3417
3418 if opts.get('create'):
3418 if opts.get('create'):
3419 if name in existing:
3419 if name in existing:
3420 raise error.Abort(_('queue "%s" already exists') % name)
3420 raise error.Abort(_('queue "%s" already exists') % name)
3421 if _noqueues():
3421 if _noqueues():
3422 _addqueue(_defaultqueue)
3422 _addqueue(_defaultqueue)
3423 _addqueue(name)
3423 _addqueue(name)
3424 _setactive(name)
3424 _setactive(name)
3425 elif opts.get('rename'):
3425 elif opts.get('rename'):
3426 current = _getcurrent()
3426 current = _getcurrent()
3427 if name == current:
3427 if name == current:
3428 raise error.Abort(_('can\'t rename "%s" to its current name')
3428 raise error.Abort(_('can\'t rename "%s" to its current name')
3429 % name)
3429 % name)
3430 if name in existing:
3430 if name in existing:
3431 raise error.Abort(_('queue "%s" already exists') % name)
3431 raise error.Abort(_('queue "%s" already exists') % name)
3432
3432
3433 olddir = _queuedir(current)
3433 olddir = _queuedir(current)
3434 newdir = _queuedir(name)
3434 newdir = _queuedir(name)
3435
3435
3436 if os.path.exists(newdir):
3436 if os.path.exists(newdir):
3437 raise error.Abort(_('non-queue directory "%s" already exists') %
3437 raise error.Abort(_('non-queue directory "%s" already exists') %
3438 newdir)
3438 newdir)
3439
3439
3440 fh = repo.vfs('patches.queues.new', 'w')
3440 fh = repo.vfs('patches.queues.new', 'w')
3441 for queue in existing:
3441 for queue in existing:
3442 if queue == current:
3442 if queue == current:
3443 fh.write('%s\n' % (name,))
3443 fh.write('%s\n' % (name,))
3444 if os.path.exists(olddir):
3444 if os.path.exists(olddir):
3445 util.rename(olddir, newdir)
3445 util.rename(olddir, newdir)
3446 else:
3446 else:
3447 fh.write('%s\n' % (queue,))
3447 fh.write('%s\n' % (queue,))
3448 fh.close()
3448 fh.close()
3449 repo.vfs.rename('patches.queues.new', _allqueues)
3449 repo.vfs.rename('patches.queues.new', _allqueues)
3450 _setactivenocheck(name)
3450 _setactivenocheck(name)
3451 elif opts.get('delete'):
3451 elif opts.get('delete'):
3452 _delete(name)
3452 _delete(name)
3453 elif opts.get('purge'):
3453 elif opts.get('purge'):
3454 if name in existing:
3454 if name in existing:
3455 _delete(name)
3455 _delete(name)
3456 qdir = _queuedir(name)
3456 qdir = _queuedir(name)
3457 if os.path.exists(qdir):
3457 if os.path.exists(qdir):
3458 shutil.rmtree(qdir)
3458 shutil.rmtree(qdir)
3459 else:
3459 else:
3460 if name not in existing:
3460 if name not in existing:
3461 raise error.Abort(_('use --create to create a new queue'))
3461 raise error.Abort(_('use --create to create a new queue'))
3462 _setactive(name)
3462 _setactive(name)
3463
3463
3464 def mqphasedefaults(repo, roots):
3464 def mqphasedefaults(repo, roots):
3465 """callback used to set mq changeset as secret when no phase data exists"""
3465 """callback used to set mq changeset as secret when no phase data exists"""
3466 if repo.mq.applied:
3466 if repo.mq.applied:
3467 if repo.ui.configbool('mq', 'secret'):
3467 if repo.ui.configbool('mq', 'secret'):
3468 mqphase = phases.secret
3468 mqphase = phases.secret
3469 else:
3469 else:
3470 mqphase = phases.draft
3470 mqphase = phases.draft
3471 qbase = repo[repo.mq.applied[0].node]
3471 qbase = repo[repo.mq.applied[0].node]
3472 roots[mqphase].add(qbase.node())
3472 roots[mqphase].add(qbase.node())
3473 return roots
3473 return roots
3474
3474
3475 def reposetup(ui, repo):
3475 def reposetup(ui, repo):
3476 class mqrepo(repo.__class__):
3476 class mqrepo(repo.__class__):
3477 @localrepo.unfilteredpropertycache
3477 @localrepo.unfilteredpropertycache
3478 def mq(self):
3478 def mq(self):
3479 return queue(self.ui, self.baseui, self.path)
3479 return queue(self.ui, self.baseui, self.path)
3480
3480
3481 def invalidateall(self):
3481 def invalidateall(self):
3482 super(mqrepo, self).invalidateall()
3482 super(mqrepo, self).invalidateall()
3483 if localrepo.hasunfilteredcache(self, 'mq'):
3483 if localrepo.hasunfilteredcache(self, 'mq'):
3484 # recreate mq in case queue path was changed
3484 # recreate mq in case queue path was changed
3485 delattr(self.unfiltered(), 'mq')
3485 delattr(self.unfiltered(), 'mq')
3486
3486
3487 def abortifwdirpatched(self, errmsg, force=False):
3487 def abortifwdirpatched(self, errmsg, force=False):
3488 if self.mq.applied and self.mq.checkapplied and not force:
3488 if self.mq.applied and self.mq.checkapplied and not force:
3489 parents = self.dirstate.parents()
3489 parents = self.dirstate.parents()
3490 patches = [s.node for s in self.mq.applied]
3490 patches = [s.node for s in self.mq.applied]
3491 if parents[0] in patches or parents[1] in patches:
3491 if parents[0] in patches or parents[1] in patches:
3492 raise error.Abort(errmsg)
3492 raise error.Abort(errmsg)
3493
3493
3494 def commit(self, text="", user=None, date=None, match=None,
3494 def commit(self, text="", user=None, date=None, match=None,
3495 force=False, editor=False, extra=None):
3495 force=False, editor=False, extra=None):
3496 if extra is None:
3496 if extra is None:
3497 extra = {}
3497 extra = {}
3498 self.abortifwdirpatched(
3498 self.abortifwdirpatched(
3499 _('cannot commit over an applied mq patch'),
3499 _('cannot commit over an applied mq patch'),
3500 force)
3500 force)
3501
3501
3502 return super(mqrepo, self).commit(text, user, date, match, force,
3502 return super(mqrepo, self).commit(text, user, date, match, force,
3503 editor, extra)
3503 editor, extra)
3504
3504
3505 def checkpush(self, pushop):
3505 def checkpush(self, pushop):
3506 if self.mq.applied and self.mq.checkapplied and not pushop.force:
3506 if self.mq.applied and self.mq.checkapplied and not pushop.force:
3507 outapplied = [e.node for e in self.mq.applied]
3507 outapplied = [e.node for e in self.mq.applied]
3508 if pushop.revs:
3508 if pushop.revs:
3509 # Assume applied patches have no non-patch descendants and
3509 # Assume applied patches have no non-patch descendants and
3510 # are not on remote already. Filtering any changeset not
3510 # are not on remote already. Filtering any changeset not
3511 # pushed.
3511 # pushed.
3512 heads = set(pushop.revs)
3512 heads = set(pushop.revs)
3513 for node in reversed(outapplied):
3513 for node in reversed(outapplied):
3514 if node in heads:
3514 if node in heads:
3515 break
3515 break
3516 else:
3516 else:
3517 outapplied.pop()
3517 outapplied.pop()
3518 # looking for pushed and shared changeset
3518 # looking for pushed and shared changeset
3519 for node in outapplied:
3519 for node in outapplied:
3520 if self[node].phase() < phases.secret:
3520 if self[node].phase() < phases.secret:
3521 raise error.Abort(_('source has mq patches applied'))
3521 raise error.Abort(_('source has mq patches applied'))
3522 # no non-secret patches pushed
3522 # no non-secret patches pushed
3523 super(mqrepo, self).checkpush(pushop)
3523 super(mqrepo, self).checkpush(pushop)
3524
3524
3525 def _findtags(self):
3525 def _findtags(self):
3526 '''augment tags from base class with patch tags'''
3526 '''augment tags from base class with patch tags'''
3527 result = super(mqrepo, self)._findtags()
3527 result = super(mqrepo, self)._findtags()
3528
3528
3529 q = self.mq
3529 q = self.mq
3530 if not q.applied:
3530 if not q.applied:
3531 return result
3531 return result
3532
3532
3533 mqtags = [(patch.node, patch.name) for patch in q.applied]
3533 mqtags = [(patch.node, patch.name) for patch in q.applied]
3534
3534
3535 try:
3535 try:
3536 # for now ignore filtering business
3536 # for now ignore filtering business
3537 self.unfiltered().changelog.rev(mqtags[-1][0])
3537 self.unfiltered().changelog.rev(mqtags[-1][0])
3538 except error.LookupError:
3538 except error.LookupError:
3539 self.ui.warn(_('mq status file refers to unknown node %s\n')
3539 self.ui.warn(_('mq status file refers to unknown node %s\n')
3540 % short(mqtags[-1][0]))
3540 % short(mqtags[-1][0]))
3541 return result
3541 return result
3542
3542
3543 # do not add fake tags for filtered revisions
3543 # do not add fake tags for filtered revisions
3544 included = self.changelog.hasnode
3544 included = self.changelog.hasnode
3545 mqtags = [mqt for mqt in mqtags if included(mqt[0])]
3545 mqtags = [mqt for mqt in mqtags if included(mqt[0])]
3546 if not mqtags:
3546 if not mqtags:
3547 return result
3547 return result
3548
3548
3549 mqtags.append((mqtags[-1][0], 'qtip'))
3549 mqtags.append((mqtags[-1][0], 'qtip'))
3550 mqtags.append((mqtags[0][0], 'qbase'))
3550 mqtags.append((mqtags[0][0], 'qbase'))
3551 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3551 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3552 tags = result[0]
3552 tags = result[0]
3553 for patch in mqtags:
3553 for patch in mqtags:
3554 if patch[1] in tags:
3554 if patch[1] in tags:
3555 self.ui.warn(_('tag %s overrides mq patch of the same '
3555 self.ui.warn(_('tag %s overrides mq patch of the same '
3556 'name\n') % patch[1])
3556 'name\n') % patch[1])
3557 else:
3557 else:
3558 tags[patch[1]] = patch[0]
3558 tags[patch[1]] = patch[0]
3559
3559
3560 return result
3560 return result
3561
3561
3562 if repo.local():
3562 if repo.local():
3563 repo.__class__ = mqrepo
3563 repo.__class__ = mqrepo
3564
3564
3565 repo._phasedefaults.append(mqphasedefaults)
3565 repo._phasedefaults.append(mqphasedefaults)
3566
3566
3567 def mqimport(orig, ui, repo, *args, **kwargs):
3567 def mqimport(orig, ui, repo, *args, **kwargs):
3568 if (util.safehasattr(repo, 'abortifwdirpatched')
3568 if (util.safehasattr(repo, 'abortifwdirpatched')
3569 and not kwargs.get(r'no_commit', False)):
3569 and not kwargs.get(r'no_commit', False)):
3570 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3570 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3571 kwargs.get(r'force'))
3571 kwargs.get(r'force'))
3572 return orig(ui, repo, *args, **kwargs)
3572 return orig(ui, repo, *args, **kwargs)
3573
3573
3574 def mqinit(orig, ui, *args, **kwargs):
3574 def mqinit(orig, ui, *args, **kwargs):
3575 mq = kwargs.pop(r'mq', None)
3575 mq = kwargs.pop(r'mq', None)
3576
3576
3577 if not mq:
3577 if not mq:
3578 return orig(ui, *args, **kwargs)
3578 return orig(ui, *args, **kwargs)
3579
3579
3580 if args:
3580 if args:
3581 repopath = args[0]
3581 repopath = args[0]
3582 if not hg.islocal(repopath):
3582 if not hg.islocal(repopath):
3583 raise error.Abort(_('only a local queue repository '
3583 raise error.Abort(_('only a local queue repository '
3584 'may be initialized'))
3584 'may be initialized'))
3585 else:
3585 else:
3586 repopath = cmdutil.findrepo(pycompat.getcwd())
3586 repopath = cmdutil.findrepo(pycompat.getcwd())
3587 if not repopath:
3587 if not repopath:
3588 raise error.Abort(_('there is no Mercurial repository here '
3588 raise error.Abort(_('there is no Mercurial repository here '
3589 '(.hg not found)'))
3589 '(.hg not found)'))
3590 repo = hg.repository(ui, repopath)
3590 repo = hg.repository(ui, repopath)
3591 return qinit(ui, repo, True)
3591 return qinit(ui, repo, True)
3592
3592
3593 def mqcommand(orig, ui, repo, *args, **kwargs):
3593 def mqcommand(orig, ui, repo, *args, **kwargs):
3594 """Add --mq option to operate on patch repository instead of main"""
3594 """Add --mq option to operate on patch repository instead of main"""
3595
3595
3596 # some commands do not like getting unknown options
3596 # some commands do not like getting unknown options
3597 mq = kwargs.pop(r'mq', None)
3597 mq = kwargs.pop(r'mq', None)
3598
3598
3599 if not mq:
3599 if not mq:
3600 return orig(ui, repo, *args, **kwargs)
3600 return orig(ui, repo, *args, **kwargs)
3601
3601
3602 q = repo.mq
3602 q = repo.mq
3603 r = q.qrepo()
3603 r = q.qrepo()
3604 if not r:
3604 if not r:
3605 raise error.Abort(_('no queue repository'))
3605 raise error.Abort(_('no queue repository'))
3606 return orig(r.ui, r, *args, **kwargs)
3606 return orig(r.ui, r, *args, **kwargs)
3607
3607
3608 def summaryhook(ui, repo):
3608 def summaryhook(ui, repo):
3609 q = repo.mq
3609 q = repo.mq
3610 m = []
3610 m = []
3611 a, u = len(q.applied), len(q.unapplied(repo))
3611 a, u = len(q.applied), len(q.unapplied(repo))
3612 if a:
3612 if a:
3613 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3613 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3614 if u:
3614 if u:
3615 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3615 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3616 if m:
3616 if m:
3617 # i18n: column positioning for "hg summary"
3617 # i18n: column positioning for "hg summary"
3618 ui.write(_("mq: %s\n") % ', '.join(m))
3618 ui.write(_("mq: %s\n") % ', '.join(m))
3619 else:
3619 else:
3620 # i18n: column positioning for "hg summary"
3620 # i18n: column positioning for "hg summary"
3621 ui.note(_("mq: (empty queue)\n"))
3621 ui.note(_("mq: (empty queue)\n"))
3622
3622
3623 revsetpredicate = registrar.revsetpredicate()
3623 revsetpredicate = registrar.revsetpredicate()
3624
3624
3625 @revsetpredicate('mq()')
3625 @revsetpredicate('mq()')
3626 def revsetmq(repo, subset, x):
3626 def revsetmq(repo, subset, x):
3627 """Changesets managed by MQ.
3627 """Changesets managed by MQ.
3628 """
3628 """
3629 revsetlang.getargs(x, 0, 0, _("mq takes no arguments"))
3629 revsetlang.getargs(x, 0, 0, _("mq takes no arguments"))
3630 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3630 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3631 return smartset.baseset([r for r in subset if r in applied])
3631 return smartset.baseset([r for r in subset if r in applied])
3632
3632
3633 # tell hggettext to extract docstrings from these functions:
3633 # tell hggettext to extract docstrings from these functions:
3634 i18nfunctions = [revsetmq]
3634 i18nfunctions = [revsetmq]
3635
3635
3636 def extsetup(ui):
3636 def extsetup(ui):
3637 # Ensure mq wrappers are called first, regardless of extension load order by
3637 # Ensure mq wrappers are called first, regardless of extension load order by
3638 # NOT wrapping in uisetup() and instead deferring to init stage two here.
3638 # NOT wrapping in uisetup() and instead deferring to init stage two here.
3639 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3639 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3640
3640
3641 extensions.wrapcommand(commands.table, 'import', mqimport)
3641 extensions.wrapcommand(commands.table, 'import', mqimport)
3642 cmdutil.summaryhooks.add('mq', summaryhook)
3642 cmdutil.summaryhooks.add('mq', summaryhook)
3643
3643
3644 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3644 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3645 entry[1].extend(mqopt)
3645 entry[1].extend(mqopt)
3646
3646
3647 def dotable(cmdtable):
3647 def dotable(cmdtable):
3648 for cmd, entry in cmdtable.iteritems():
3648 for cmd, entry in cmdtable.iteritems():
3649 cmd = cmdutil.parsealiases(cmd)[0]
3649 cmd = cmdutil.parsealiases(cmd)[0]
3650 func = entry[0]
3650 func = entry[0]
3651 if func.norepo:
3651 if func.norepo:
3652 continue
3652 continue
3653 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3653 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3654 entry[1].extend(mqopt)
3654 entry[1].extend(mqopt)
3655
3655
3656 dotable(commands.table)
3656 dotable(commands.table)
3657
3657
3658 for extname, extmodule in extensions.extensions():
3658 for extname, extmodule in extensions.extensions():
3659 if extmodule.__file__ != __file__:
3659 if extmodule.__file__ != __file__:
3660 dotable(getattr(extmodule, 'cmdtable', {}))
3660 dotable(getattr(extmodule, 'cmdtable', {}))
3661
3661
3662 colortable = {'qguard.negative': 'red',
3662 colortable = {'qguard.negative': 'red',
3663 'qguard.positive': 'yellow',
3663 'qguard.positive': 'yellow',
3664 'qguard.unguarded': 'green',
3664 'qguard.unguarded': 'green',
3665 'qseries.applied': 'blue bold underline',
3665 'qseries.applied': 'blue bold underline',
3666 'qseries.guarded': 'black bold',
3666 'qseries.guarded': 'black bold',
3667 'qseries.missing': 'red bold',
3667 'qseries.missing': 'red bold',
3668 'qseries.unapplied': 'black bold'}
3668 'qseries.unapplied': 'black bold'}
General Comments 0
You need to be logged in to leave comments. Login now