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