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