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