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