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