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