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