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