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