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