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