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