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