##// END OF EJS Templates
strip: add optional -r to synopsis
Thomas Arendsen Hein -
r16989:0b1d1519 default
parent child Browse files
Show More
@@ -1,3574 +1,3574 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, url, error, phases
66 from mercurial import repair, extensions, url, error, phases
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):
936 def checksubstate(self, repo):
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 for s in wctx.substate:
941 for s in wctx.substate:
942 if wctx.sub(s).dirty(True):
942 if wctx.sub(s).dirty(True):
943 raise util.Abort(
943 raise util.Abort(
944 _("uncommitted changes in subrepository %s") % s)
944 _("uncommitted changes in subrepository %s") % s)
945 elif wctx.sub(s).dirty():
945 elif wctx.sub(s).dirty():
946 inclsubs.append(s)
946 inclsubs.append(s)
947 return inclsubs
947 return inclsubs
948
948
949 def localchangesfound(self, refresh=True):
949 def localchangesfound(self, refresh=True):
950 if refresh:
950 if refresh:
951 raise util.Abort(_("local changes found, refresh first"))
951 raise util.Abort(_("local changes found, refresh first"))
952 else:
952 else:
953 raise util.Abort(_("local changes found"))
953 raise util.Abort(_("local changes found"))
954
954
955 def checklocalchanges(self, repo, force=False, refresh=True):
955 def checklocalchanges(self, repo, force=False, refresh=True):
956 m, a, r, d = repo.status()[:4]
956 m, a, r, d = repo.status()[:4]
957 if (m or a or r or d) and not force:
957 if (m or a or r or d) and not force:
958 self.localchangesfound(refresh)
958 self.localchangesfound(refresh)
959 return m, a, r, d
959 return m, a, r, d
960
960
961 _reserved = ('series', 'status', 'guards', '.', '..')
961 _reserved = ('series', 'status', 'guards', '.', '..')
962 def checkreservedname(self, name):
962 def checkreservedname(self, name):
963 if name in self._reserved:
963 if name in self._reserved:
964 raise util.Abort(_('"%s" cannot be used as the name of a patch')
964 raise util.Abort(_('"%s" cannot be used as the name of a patch')
965 % name)
965 % name)
966 for prefix in ('.hg', '.mq'):
966 for prefix in ('.hg', '.mq'):
967 if name.startswith(prefix):
967 if name.startswith(prefix):
968 raise util.Abort(_('patch name cannot begin with "%s"')
968 raise util.Abort(_('patch name cannot begin with "%s"')
969 % prefix)
969 % prefix)
970 for c in ('#', ':'):
970 for c in ('#', ':'):
971 if c in name:
971 if c in name:
972 raise util.Abort(_('"%s" cannot be used in the name of a patch')
972 raise util.Abort(_('"%s" cannot be used in the name of a patch')
973 % c)
973 % c)
974
974
975 def checkpatchname(self, name, force=False):
975 def checkpatchname(self, name, force=False):
976 self.checkreservedname(name)
976 self.checkreservedname(name)
977 if not force and os.path.exists(self.join(name)):
977 if not force and os.path.exists(self.join(name)):
978 if os.path.isdir(self.join(name)):
978 if os.path.isdir(self.join(name)):
979 raise util.Abort(_('"%s" already exists as a directory')
979 raise util.Abort(_('"%s" already exists as a directory')
980 % name)
980 % name)
981 else:
981 else:
982 raise util.Abort(_('patch "%s" already exists') % name)
982 raise util.Abort(_('patch "%s" already exists') % name)
983
983
984 def checkkeepchanges(self, keepchanges, force):
984 def checkkeepchanges(self, keepchanges, force):
985 if force and keepchanges:
985 if force and keepchanges:
986 raise util.Abort(_('cannot use both --force and --keep-changes'))
986 raise util.Abort(_('cannot use both --force and --keep-changes'))
987
987
988 def new(self, repo, patchfn, *pats, **opts):
988 def new(self, repo, patchfn, *pats, **opts):
989 """options:
989 """options:
990 msg: a string or a no-argument function returning a string
990 msg: a string or a no-argument function returning a string
991 """
991 """
992 msg = opts.get('msg')
992 msg = opts.get('msg')
993 user = opts.get('user')
993 user = opts.get('user')
994 date = opts.get('date')
994 date = opts.get('date')
995 if date:
995 if date:
996 date = util.parsedate(date)
996 date = util.parsedate(date)
997 diffopts = self.diffopts({'git': opts.get('git')})
997 diffopts = self.diffopts({'git': opts.get('git')})
998 if opts.get('checkname', True):
998 if opts.get('checkname', True):
999 self.checkpatchname(patchfn)
999 self.checkpatchname(patchfn)
1000 inclsubs = self.checksubstate(repo)
1000 inclsubs = self.checksubstate(repo)
1001 if inclsubs:
1001 if inclsubs:
1002 inclsubs.append('.hgsubstate')
1002 inclsubs.append('.hgsubstate')
1003 substatestate = repo.dirstate['.hgsubstate']
1003 substatestate = repo.dirstate['.hgsubstate']
1004 if opts.get('include') or opts.get('exclude') or pats:
1004 if opts.get('include') or opts.get('exclude') or pats:
1005 if inclsubs:
1005 if inclsubs:
1006 pats = list(pats or []) + inclsubs
1006 pats = list(pats or []) + inclsubs
1007 match = scmutil.match(repo[None], pats, opts)
1007 match = scmutil.match(repo[None], pats, opts)
1008 # detect missing files in pats
1008 # detect missing files in pats
1009 def badfn(f, msg):
1009 def badfn(f, msg):
1010 if f != '.hgsubstate': # .hgsubstate is auto-created
1010 if f != '.hgsubstate': # .hgsubstate is auto-created
1011 raise util.Abort('%s: %s' % (f, msg))
1011 raise util.Abort('%s: %s' % (f, msg))
1012 match.bad = badfn
1012 match.bad = badfn
1013 changes = repo.status(match=match)
1013 changes = repo.status(match=match)
1014 m, a, r, d = changes[:4]
1014 m, a, r, d = changes[:4]
1015 else:
1015 else:
1016 changes = self.checklocalchanges(repo, force=True)
1016 changes = self.checklocalchanges(repo, force=True)
1017 m, a, r, d = changes
1017 m, a, r, d = changes
1018 match = scmutil.matchfiles(repo, m + a + r + inclsubs)
1018 match = scmutil.matchfiles(repo, m + a + r + inclsubs)
1019 if len(repo[None].parents()) > 1:
1019 if len(repo[None].parents()) > 1:
1020 raise util.Abort(_('cannot manage merge changesets'))
1020 raise util.Abort(_('cannot manage merge changesets'))
1021 commitfiles = m + a + r
1021 commitfiles = m + a + r
1022 self.checktoppatch(repo)
1022 self.checktoppatch(repo)
1023 insert = self.fullseriesend()
1023 insert = self.fullseriesend()
1024 wlock = repo.wlock()
1024 wlock = repo.wlock()
1025 try:
1025 try:
1026 try:
1026 try:
1027 # if patch file write fails, abort early
1027 # if patch file write fails, abort early
1028 p = self.opener(patchfn, "w")
1028 p = self.opener(patchfn, "w")
1029 except IOError, e:
1029 except IOError, e:
1030 raise util.Abort(_('cannot write patch "%s": %s')
1030 raise util.Abort(_('cannot write patch "%s": %s')
1031 % (patchfn, e.strerror))
1031 % (patchfn, e.strerror))
1032 try:
1032 try:
1033 if self.plainmode:
1033 if self.plainmode:
1034 if user:
1034 if user:
1035 p.write("From: " + user + "\n")
1035 p.write("From: " + user + "\n")
1036 if not date:
1036 if not date:
1037 p.write("\n")
1037 p.write("\n")
1038 if date:
1038 if date:
1039 p.write("Date: %d %d\n\n" % date)
1039 p.write("Date: %d %d\n\n" % date)
1040 else:
1040 else:
1041 p.write("# HG changeset patch\n")
1041 p.write("# HG changeset patch\n")
1042 p.write("# Parent "
1042 p.write("# Parent "
1043 + hex(repo[None].p1().node()) + "\n")
1043 + hex(repo[None].p1().node()) + "\n")
1044 if user:
1044 if user:
1045 p.write("# User " + user + "\n")
1045 p.write("# User " + user + "\n")
1046 if date:
1046 if date:
1047 p.write("# Date %s %s\n\n" % date)
1047 p.write("# Date %s %s\n\n" % date)
1048 if util.safehasattr(msg, '__call__'):
1048 if util.safehasattr(msg, '__call__'):
1049 msg = msg()
1049 msg = msg()
1050 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
1050 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
1051 n = newcommit(repo, None, commitmsg, user, date, match=match,
1051 n = newcommit(repo, None, commitmsg, user, date, match=match,
1052 force=True)
1052 force=True)
1053 if n is None:
1053 if n is None:
1054 raise util.Abort(_("repo commit failed"))
1054 raise util.Abort(_("repo commit failed"))
1055 try:
1055 try:
1056 self.fullseries[insert:insert] = [patchfn]
1056 self.fullseries[insert:insert] = [patchfn]
1057 self.applied.append(statusentry(n, patchfn))
1057 self.applied.append(statusentry(n, patchfn))
1058 self.parseseries()
1058 self.parseseries()
1059 self.seriesdirty = True
1059 self.seriesdirty = True
1060 self.applieddirty = True
1060 self.applieddirty = True
1061 if msg:
1061 if msg:
1062 msg = msg + "\n\n"
1062 msg = msg + "\n\n"
1063 p.write(msg)
1063 p.write(msg)
1064 if commitfiles:
1064 if commitfiles:
1065 parent = self.qparents(repo, n)
1065 parent = self.qparents(repo, n)
1066 if inclsubs:
1066 if inclsubs:
1067 if substatestate in 'a?':
1067 if substatestate in 'a?':
1068 changes[1].append('.hgsubstate')
1068 changes[1].append('.hgsubstate')
1069 elif substatestate in 'r':
1069 elif substatestate in 'r':
1070 changes[2].append('.hgsubstate')
1070 changes[2].append('.hgsubstate')
1071 else: # modified
1071 else: # modified
1072 changes[0].append('.hgsubstate')
1072 changes[0].append('.hgsubstate')
1073 chunks = patchmod.diff(repo, node1=parent, node2=n,
1073 chunks = patchmod.diff(repo, node1=parent, node2=n,
1074 changes=changes, opts=diffopts)
1074 changes=changes, opts=diffopts)
1075 for chunk in chunks:
1075 for chunk in chunks:
1076 p.write(chunk)
1076 p.write(chunk)
1077 p.close()
1077 p.close()
1078 r = self.qrepo()
1078 r = self.qrepo()
1079 if r:
1079 if r:
1080 r[None].add([patchfn])
1080 r[None].add([patchfn])
1081 except: # re-raises
1081 except: # re-raises
1082 repo.rollback()
1082 repo.rollback()
1083 raise
1083 raise
1084 except Exception:
1084 except Exception:
1085 patchpath = self.join(patchfn)
1085 patchpath = self.join(patchfn)
1086 try:
1086 try:
1087 os.unlink(patchpath)
1087 os.unlink(patchpath)
1088 except OSError:
1088 except OSError:
1089 self.ui.warn(_('error unlinking %s\n') % patchpath)
1089 self.ui.warn(_('error unlinking %s\n') % patchpath)
1090 raise
1090 raise
1091 self.removeundo(repo)
1091 self.removeundo(repo)
1092 finally:
1092 finally:
1093 release(wlock)
1093 release(wlock)
1094
1094
1095 def strip(self, repo, revs, update=True, backup="all", force=None):
1095 def strip(self, repo, revs, update=True, backup="all", force=None):
1096 wlock = lock = None
1096 wlock = lock = None
1097 try:
1097 try:
1098 wlock = repo.wlock()
1098 wlock = repo.wlock()
1099 lock = repo.lock()
1099 lock = repo.lock()
1100
1100
1101 if update:
1101 if update:
1102 self.checklocalchanges(repo, force=force, refresh=False)
1102 self.checklocalchanges(repo, force=force, refresh=False)
1103 urev = self.qparents(repo, revs[0])
1103 urev = self.qparents(repo, revs[0])
1104 hg.clean(repo, urev)
1104 hg.clean(repo, urev)
1105 repo.dirstate.write()
1105 repo.dirstate.write()
1106
1106
1107 repair.strip(self.ui, repo, revs, backup)
1107 repair.strip(self.ui, repo, revs, backup)
1108 finally:
1108 finally:
1109 release(lock, wlock)
1109 release(lock, wlock)
1110
1110
1111 def isapplied(self, patch):
1111 def isapplied(self, patch):
1112 """returns (index, rev, patch)"""
1112 """returns (index, rev, patch)"""
1113 for i, a in enumerate(self.applied):
1113 for i, a in enumerate(self.applied):
1114 if a.name == patch:
1114 if a.name == patch:
1115 return (i, a.node, a.name)
1115 return (i, a.node, a.name)
1116 return None
1116 return None
1117
1117
1118 # if the exact patch name does not exist, we try a few
1118 # if the exact patch name does not exist, we try a few
1119 # variations. If strict is passed, we try only #1
1119 # variations. If strict is passed, we try only #1
1120 #
1120 #
1121 # 1) a number (as string) to indicate an offset in the series file
1121 # 1) a number (as string) to indicate an offset in the series file
1122 # 2) a unique substring of the patch name was given
1122 # 2) a unique substring of the patch name was given
1123 # 3) patchname[-+]num to indicate an offset in the series file
1123 # 3) patchname[-+]num to indicate an offset in the series file
1124 def lookup(self, patch, strict=False):
1124 def lookup(self, patch, strict=False):
1125 def partialname(s):
1125 def partialname(s):
1126 if s in self.series:
1126 if s in self.series:
1127 return s
1127 return s
1128 matches = [x for x in self.series if s in x]
1128 matches = [x for x in self.series if s in x]
1129 if len(matches) > 1:
1129 if len(matches) > 1:
1130 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1130 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1131 for m in matches:
1131 for m in matches:
1132 self.ui.warn(' %s\n' % m)
1132 self.ui.warn(' %s\n' % m)
1133 return None
1133 return None
1134 if matches:
1134 if matches:
1135 return matches[0]
1135 return matches[0]
1136 if self.series and self.applied:
1136 if self.series and self.applied:
1137 if s == 'qtip':
1137 if s == 'qtip':
1138 return self.series[self.seriesend(True)-1]
1138 return self.series[self.seriesend(True)-1]
1139 if s == 'qbase':
1139 if s == 'qbase':
1140 return self.series[0]
1140 return self.series[0]
1141 return None
1141 return None
1142
1142
1143 if patch in self.series:
1143 if patch in self.series:
1144 return patch
1144 return patch
1145
1145
1146 if not os.path.isfile(self.join(patch)):
1146 if not os.path.isfile(self.join(patch)):
1147 try:
1147 try:
1148 sno = int(patch)
1148 sno = int(patch)
1149 except (ValueError, OverflowError):
1149 except (ValueError, OverflowError):
1150 pass
1150 pass
1151 else:
1151 else:
1152 if -len(self.series) <= sno < len(self.series):
1152 if -len(self.series) <= sno < len(self.series):
1153 return self.series[sno]
1153 return self.series[sno]
1154
1154
1155 if not strict:
1155 if not strict:
1156 res = partialname(patch)
1156 res = partialname(patch)
1157 if res:
1157 if res:
1158 return res
1158 return res
1159 minus = patch.rfind('-')
1159 minus = patch.rfind('-')
1160 if minus >= 0:
1160 if minus >= 0:
1161 res = partialname(patch[:minus])
1161 res = partialname(patch[:minus])
1162 if res:
1162 if res:
1163 i = self.series.index(res)
1163 i = self.series.index(res)
1164 try:
1164 try:
1165 off = int(patch[minus + 1:] or 1)
1165 off = int(patch[minus + 1:] or 1)
1166 except (ValueError, OverflowError):
1166 except (ValueError, OverflowError):
1167 pass
1167 pass
1168 else:
1168 else:
1169 if i - off >= 0:
1169 if i - off >= 0:
1170 return self.series[i - off]
1170 return self.series[i - off]
1171 plus = patch.rfind('+')
1171 plus = patch.rfind('+')
1172 if plus >= 0:
1172 if plus >= 0:
1173 res = partialname(patch[:plus])
1173 res = partialname(patch[:plus])
1174 if res:
1174 if res:
1175 i = self.series.index(res)
1175 i = self.series.index(res)
1176 try:
1176 try:
1177 off = int(patch[plus + 1:] or 1)
1177 off = int(patch[plus + 1:] or 1)
1178 except (ValueError, OverflowError):
1178 except (ValueError, OverflowError):
1179 pass
1179 pass
1180 else:
1180 else:
1181 if i + off < len(self.series):
1181 if i + off < len(self.series):
1182 return self.series[i + off]
1182 return self.series[i + off]
1183 raise util.Abort(_("patch %s not in series") % patch)
1183 raise util.Abort(_("patch %s not in series") % patch)
1184
1184
1185 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1185 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1186 all=False, move=False, exact=False, nobackup=False,
1186 all=False, move=False, exact=False, nobackup=False,
1187 keepchanges=False):
1187 keepchanges=False):
1188 self.checkkeepchanges(keepchanges, force)
1188 self.checkkeepchanges(keepchanges, force)
1189 diffopts = self.diffopts()
1189 diffopts = self.diffopts()
1190 wlock = repo.wlock()
1190 wlock = repo.wlock()
1191 try:
1191 try:
1192 heads = []
1192 heads = []
1193 for b, ls in repo.branchmap().iteritems():
1193 for b, ls in repo.branchmap().iteritems():
1194 heads += ls
1194 heads += ls
1195 if not heads:
1195 if not heads:
1196 heads = [nullid]
1196 heads = [nullid]
1197 if repo.dirstate.p1() not in heads and not exact:
1197 if repo.dirstate.p1() not in heads and not exact:
1198 self.ui.status(_("(working directory not at a head)\n"))
1198 self.ui.status(_("(working directory not at a head)\n"))
1199
1199
1200 if not self.series:
1200 if not self.series:
1201 self.ui.warn(_('no patches in series\n'))
1201 self.ui.warn(_('no patches in series\n'))
1202 return 0
1202 return 0
1203
1203
1204 # Suppose our series file is: A B C and the current 'top'
1204 # Suppose our series file is: A B C and the current 'top'
1205 # patch is B. qpush C should be performed (moving forward)
1205 # patch is B. qpush C should be performed (moving forward)
1206 # qpush B is a NOP (no change) qpush A is an error (can't
1206 # qpush B is a NOP (no change) qpush A is an error (can't
1207 # go backwards with qpush)
1207 # go backwards with qpush)
1208 if patch:
1208 if patch:
1209 patch = self.lookup(patch)
1209 patch = self.lookup(patch)
1210 info = self.isapplied(patch)
1210 info = self.isapplied(patch)
1211 if info and info[0] >= len(self.applied) - 1:
1211 if info and info[0] >= len(self.applied) - 1:
1212 self.ui.warn(
1212 self.ui.warn(
1213 _('qpush: %s is already at the top\n') % patch)
1213 _('qpush: %s is already at the top\n') % patch)
1214 return 0
1214 return 0
1215
1215
1216 pushable, reason = self.pushable(patch)
1216 pushable, reason = self.pushable(patch)
1217 if pushable:
1217 if pushable:
1218 if self.series.index(patch) < self.seriesend():
1218 if self.series.index(patch) < self.seriesend():
1219 raise util.Abort(
1219 raise util.Abort(
1220 _("cannot push to a previous patch: %s") % patch)
1220 _("cannot push to a previous patch: %s") % patch)
1221 else:
1221 else:
1222 if reason:
1222 if reason:
1223 reason = _('guarded by %s') % reason
1223 reason = _('guarded by %s') % reason
1224 else:
1224 else:
1225 reason = _('no matching guards')
1225 reason = _('no matching guards')
1226 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1226 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1227 return 1
1227 return 1
1228 elif all:
1228 elif all:
1229 patch = self.series[-1]
1229 patch = self.series[-1]
1230 if self.isapplied(patch):
1230 if self.isapplied(patch):
1231 self.ui.warn(_('all patches are currently applied\n'))
1231 self.ui.warn(_('all patches are currently applied\n'))
1232 return 0
1232 return 0
1233
1233
1234 # Following the above example, starting at 'top' of B:
1234 # Following the above example, starting at 'top' of B:
1235 # qpush should be performed (pushes C), but a subsequent
1235 # qpush should be performed (pushes C), but a subsequent
1236 # qpush without an argument is an error (nothing to
1236 # qpush without an argument is an error (nothing to
1237 # apply). This allows a loop of "...while hg qpush..." to
1237 # apply). This allows a loop of "...while hg qpush..." to
1238 # work as it detects an error when done
1238 # work as it detects an error when done
1239 start = self.seriesend()
1239 start = self.seriesend()
1240 if start == len(self.series):
1240 if start == len(self.series):
1241 self.ui.warn(_('patch series already fully applied\n'))
1241 self.ui.warn(_('patch series already fully applied\n'))
1242 return 1
1242 return 1
1243 if not force and not keepchanges:
1243 if not force and not keepchanges:
1244 self.checklocalchanges(repo, refresh=self.applied)
1244 self.checklocalchanges(repo, refresh=self.applied)
1245
1245
1246 if exact:
1246 if exact:
1247 if keepchanges:
1247 if keepchanges:
1248 raise util.Abort(
1248 raise util.Abort(
1249 _("cannot use --exact and --keep-changes together"))
1249 _("cannot use --exact and --keep-changes together"))
1250 if move:
1250 if move:
1251 raise util.Abort(_('cannot use --exact and --move '
1251 raise util.Abort(_('cannot use --exact and --move '
1252 'together'))
1252 'together'))
1253 if self.applied:
1253 if self.applied:
1254 raise util.Abort(_('cannot push --exact with applied '
1254 raise util.Abort(_('cannot push --exact with applied '
1255 'patches'))
1255 'patches'))
1256 root = self.series[start]
1256 root = self.series[start]
1257 target = patchheader(self.join(root), self.plainmode).parent
1257 target = patchheader(self.join(root), self.plainmode).parent
1258 if not target:
1258 if not target:
1259 raise util.Abort(
1259 raise util.Abort(
1260 _("%s does not have a parent recorded") % root)
1260 _("%s does not have a parent recorded") % root)
1261 if not repo[target] == repo['.']:
1261 if not repo[target] == repo['.']:
1262 hg.update(repo, target)
1262 hg.update(repo, target)
1263
1263
1264 if move:
1264 if move:
1265 if not patch:
1265 if not patch:
1266 raise util.Abort(_("please specify the patch to move"))
1266 raise util.Abort(_("please specify the patch to move"))
1267 for fullstart, rpn in enumerate(self.fullseries):
1267 for fullstart, rpn in enumerate(self.fullseries):
1268 # strip markers for patch guards
1268 # strip markers for patch guards
1269 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1269 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1270 break
1270 break
1271 for i, rpn in enumerate(self.fullseries[fullstart:]):
1271 for i, rpn in enumerate(self.fullseries[fullstart:]):
1272 # strip markers for patch guards
1272 # strip markers for patch guards
1273 if self.guard_re.split(rpn, 1)[0] == patch:
1273 if self.guard_re.split(rpn, 1)[0] == patch:
1274 break
1274 break
1275 index = fullstart + i
1275 index = fullstart + i
1276 assert index < len(self.fullseries)
1276 assert index < len(self.fullseries)
1277 fullpatch = self.fullseries[index]
1277 fullpatch = self.fullseries[index]
1278 del self.fullseries[index]
1278 del self.fullseries[index]
1279 self.fullseries.insert(fullstart, fullpatch)
1279 self.fullseries.insert(fullstart, fullpatch)
1280 self.parseseries()
1280 self.parseseries()
1281 self.seriesdirty = True
1281 self.seriesdirty = True
1282
1282
1283 self.applieddirty = True
1283 self.applieddirty = True
1284 if start > 0:
1284 if start > 0:
1285 self.checktoppatch(repo)
1285 self.checktoppatch(repo)
1286 if not patch:
1286 if not patch:
1287 patch = self.series[start]
1287 patch = self.series[start]
1288 end = start + 1
1288 end = start + 1
1289 else:
1289 else:
1290 end = self.series.index(patch, start) + 1
1290 end = self.series.index(patch, start) + 1
1291
1291
1292 tobackup = set()
1292 tobackup = set()
1293 if (not nobackup and force) or keepchanges:
1293 if (not nobackup and force) or keepchanges:
1294 m, a, r, d = self.checklocalchanges(repo, force=True)
1294 m, a, r, d = self.checklocalchanges(repo, force=True)
1295 if keepchanges:
1295 if keepchanges:
1296 tobackup.update(m + a + r + d)
1296 tobackup.update(m + a + r + d)
1297 else:
1297 else:
1298 tobackup.update(m + a)
1298 tobackup.update(m + a)
1299
1299
1300 s = self.series[start:end]
1300 s = self.series[start:end]
1301 all_files = set()
1301 all_files = set()
1302 try:
1302 try:
1303 if mergeq:
1303 if mergeq:
1304 ret = self.mergepatch(repo, mergeq, s, diffopts)
1304 ret = self.mergepatch(repo, mergeq, s, diffopts)
1305 else:
1305 else:
1306 ret = self.apply(repo, s, list, all_files=all_files,
1306 ret = self.apply(repo, s, list, all_files=all_files,
1307 tobackup=tobackup, keepchanges=keepchanges)
1307 tobackup=tobackup, keepchanges=keepchanges)
1308 except: # re-raises
1308 except: # re-raises
1309 self.ui.warn(_('cleaning up working directory...'))
1309 self.ui.warn(_('cleaning up working directory...'))
1310 node = repo.dirstate.p1()
1310 node = repo.dirstate.p1()
1311 hg.revert(repo, node, None)
1311 hg.revert(repo, node, None)
1312 # only remove unknown files that we know we touched or
1312 # only remove unknown files that we know we touched or
1313 # created while patching
1313 # created while patching
1314 for f in all_files:
1314 for f in all_files:
1315 if f not in repo.dirstate:
1315 if f not in repo.dirstate:
1316 try:
1316 try:
1317 util.unlinkpath(repo.wjoin(f))
1317 util.unlinkpath(repo.wjoin(f))
1318 except OSError, inst:
1318 except OSError, inst:
1319 if inst.errno != errno.ENOENT:
1319 if inst.errno != errno.ENOENT:
1320 raise
1320 raise
1321 self.ui.warn(_('done\n'))
1321 self.ui.warn(_('done\n'))
1322 raise
1322 raise
1323
1323
1324 if not self.applied:
1324 if not self.applied:
1325 return ret[0]
1325 return ret[0]
1326 top = self.applied[-1].name
1326 top = self.applied[-1].name
1327 if ret[0] and ret[0] > 1:
1327 if ret[0] and ret[0] > 1:
1328 msg = _("errors during apply, please fix and refresh %s\n")
1328 msg = _("errors during apply, please fix and refresh %s\n")
1329 self.ui.write(msg % top)
1329 self.ui.write(msg % top)
1330 else:
1330 else:
1331 self.ui.write(_("now at: %s\n") % top)
1331 self.ui.write(_("now at: %s\n") % top)
1332 return ret[0]
1332 return ret[0]
1333
1333
1334 finally:
1334 finally:
1335 wlock.release()
1335 wlock.release()
1336
1336
1337 def pop(self, repo, patch=None, force=False, update=True, all=False,
1337 def pop(self, repo, patch=None, force=False, update=True, all=False,
1338 nobackup=False, keepchanges=False):
1338 nobackup=False, keepchanges=False):
1339 self.checkkeepchanges(keepchanges, force)
1339 self.checkkeepchanges(keepchanges, force)
1340 wlock = repo.wlock()
1340 wlock = repo.wlock()
1341 try:
1341 try:
1342 if patch:
1342 if patch:
1343 # index, rev, patch
1343 # index, rev, patch
1344 info = self.isapplied(patch)
1344 info = self.isapplied(patch)
1345 if not info:
1345 if not info:
1346 patch = self.lookup(patch)
1346 patch = self.lookup(patch)
1347 info = self.isapplied(patch)
1347 info = self.isapplied(patch)
1348 if not info:
1348 if not info:
1349 raise util.Abort(_("patch %s is not applied") % patch)
1349 raise util.Abort(_("patch %s is not applied") % patch)
1350
1350
1351 if not self.applied:
1351 if not self.applied:
1352 # Allow qpop -a to work repeatedly,
1352 # Allow qpop -a to work repeatedly,
1353 # but not qpop without an argument
1353 # but not qpop without an argument
1354 self.ui.warn(_("no patches applied\n"))
1354 self.ui.warn(_("no patches applied\n"))
1355 return not all
1355 return not all
1356
1356
1357 if all:
1357 if all:
1358 start = 0
1358 start = 0
1359 elif patch:
1359 elif patch:
1360 start = info[0] + 1
1360 start = info[0] + 1
1361 else:
1361 else:
1362 start = len(self.applied) - 1
1362 start = len(self.applied) - 1
1363
1363
1364 if start >= len(self.applied):
1364 if start >= len(self.applied):
1365 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1365 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1366 return
1366 return
1367
1367
1368 if not update:
1368 if not update:
1369 parents = repo.dirstate.parents()
1369 parents = repo.dirstate.parents()
1370 rr = [x.node for x in self.applied]
1370 rr = [x.node for x in self.applied]
1371 for p in parents:
1371 for p in parents:
1372 if p in rr:
1372 if p in rr:
1373 self.ui.warn(_("qpop: forcing dirstate update\n"))
1373 self.ui.warn(_("qpop: forcing dirstate update\n"))
1374 update = True
1374 update = True
1375 else:
1375 else:
1376 parents = [p.node() for p in repo[None].parents()]
1376 parents = [p.node() for p in repo[None].parents()]
1377 needupdate = False
1377 needupdate = False
1378 for entry in self.applied[start:]:
1378 for entry in self.applied[start:]:
1379 if entry.node in parents:
1379 if entry.node in parents:
1380 needupdate = True
1380 needupdate = True
1381 break
1381 break
1382 update = needupdate
1382 update = needupdate
1383
1383
1384 tobackup = set()
1384 tobackup = set()
1385 if update:
1385 if update:
1386 m, a, r, d = self.checklocalchanges(
1386 m, a, r, d = self.checklocalchanges(
1387 repo, force=force or keepchanges)
1387 repo, force=force or keepchanges)
1388 if force:
1388 if force:
1389 if not nobackup:
1389 if not nobackup:
1390 tobackup.update(m + a)
1390 tobackup.update(m + a)
1391 elif keepchanges:
1391 elif keepchanges:
1392 tobackup.update(m + a + r + d)
1392 tobackup.update(m + a + r + d)
1393
1393
1394 self.applieddirty = True
1394 self.applieddirty = True
1395 end = len(self.applied)
1395 end = len(self.applied)
1396 rev = self.applied[start].node
1396 rev = self.applied[start].node
1397 if update:
1397 if update:
1398 top = self.checktoppatch(repo)[0]
1398 top = self.checktoppatch(repo)[0]
1399
1399
1400 try:
1400 try:
1401 heads = repo.changelog.heads(rev)
1401 heads = repo.changelog.heads(rev)
1402 except error.LookupError:
1402 except error.LookupError:
1403 node = short(rev)
1403 node = short(rev)
1404 raise util.Abort(_('trying to pop unknown node %s') % node)
1404 raise util.Abort(_('trying to pop unknown node %s') % node)
1405
1405
1406 if heads != [self.applied[-1].node]:
1406 if heads != [self.applied[-1].node]:
1407 raise util.Abort(_("popping would remove a revision not "
1407 raise util.Abort(_("popping would remove a revision not "
1408 "managed by this patch queue"))
1408 "managed by this patch queue"))
1409 if not repo[self.applied[-1].node].mutable():
1409 if not repo[self.applied[-1].node].mutable():
1410 raise util.Abort(
1410 raise util.Abort(
1411 _("popping would remove an immutable revision"),
1411 _("popping would remove an immutable revision"),
1412 hint=_('see "hg help phases" for details'))
1412 hint=_('see "hg help phases" for details'))
1413
1413
1414 # we know there are no local changes, so we can make a simplified
1414 # we know there are no local changes, so we can make a simplified
1415 # form of hg.update.
1415 # form of hg.update.
1416 if update:
1416 if update:
1417 qp = self.qparents(repo, rev)
1417 qp = self.qparents(repo, rev)
1418 ctx = repo[qp]
1418 ctx = repo[qp]
1419 m, a, r, d = repo.status(qp, top)[:4]
1419 m, a, r, d = repo.status(qp, top)[:4]
1420 if d:
1420 if d:
1421 raise util.Abort(_("deletions found between repo revs"))
1421 raise util.Abort(_("deletions found between repo revs"))
1422
1422
1423 tobackup = set(a + m + r) & tobackup
1423 tobackup = set(a + m + r) & tobackup
1424 if keepchanges and tobackup:
1424 if keepchanges and tobackup:
1425 self.localchangesfound()
1425 self.localchangesfound()
1426 self.backup(repo, tobackup)
1426 self.backup(repo, tobackup)
1427
1427
1428 for f in a:
1428 for f in a:
1429 try:
1429 try:
1430 util.unlinkpath(repo.wjoin(f))
1430 util.unlinkpath(repo.wjoin(f))
1431 except OSError, e:
1431 except OSError, e:
1432 if e.errno != errno.ENOENT:
1432 if e.errno != errno.ENOENT:
1433 raise
1433 raise
1434 repo.dirstate.drop(f)
1434 repo.dirstate.drop(f)
1435 for f in m + r:
1435 for f in m + r:
1436 fctx = ctx[f]
1436 fctx = ctx[f]
1437 repo.wwrite(f, fctx.data(), fctx.flags())
1437 repo.wwrite(f, fctx.data(), fctx.flags())
1438 repo.dirstate.normal(f)
1438 repo.dirstate.normal(f)
1439 repo.setparents(qp, nullid)
1439 repo.setparents(qp, nullid)
1440 for patch in reversed(self.applied[start:end]):
1440 for patch in reversed(self.applied[start:end]):
1441 self.ui.status(_("popping %s\n") % patch.name)
1441 self.ui.status(_("popping %s\n") % patch.name)
1442 del self.applied[start:end]
1442 del self.applied[start:end]
1443 self.strip(repo, [rev], update=False, backup='strip')
1443 self.strip(repo, [rev], update=False, backup='strip')
1444 if self.applied:
1444 if self.applied:
1445 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1445 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1446 else:
1446 else:
1447 self.ui.write(_("patch queue now empty\n"))
1447 self.ui.write(_("patch queue now empty\n"))
1448 finally:
1448 finally:
1449 wlock.release()
1449 wlock.release()
1450
1450
1451 def diff(self, repo, pats, opts):
1451 def diff(self, repo, pats, opts):
1452 top, patch = self.checktoppatch(repo)
1452 top, patch = self.checktoppatch(repo)
1453 if not top:
1453 if not top:
1454 self.ui.write(_("no patches applied\n"))
1454 self.ui.write(_("no patches applied\n"))
1455 return
1455 return
1456 qp = self.qparents(repo, top)
1456 qp = self.qparents(repo, top)
1457 if opts.get('reverse'):
1457 if opts.get('reverse'):
1458 node1, node2 = None, qp
1458 node1, node2 = None, qp
1459 else:
1459 else:
1460 node1, node2 = qp, None
1460 node1, node2 = qp, None
1461 diffopts = self.diffopts(opts, patch)
1461 diffopts = self.diffopts(opts, patch)
1462 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1462 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1463
1463
1464 def refresh(self, repo, pats=None, **opts):
1464 def refresh(self, repo, pats=None, **opts):
1465 if not self.applied:
1465 if not self.applied:
1466 self.ui.write(_("no patches applied\n"))
1466 self.ui.write(_("no patches applied\n"))
1467 return 1
1467 return 1
1468 msg = opts.get('msg', '').rstrip()
1468 msg = opts.get('msg', '').rstrip()
1469 newuser = opts.get('user')
1469 newuser = opts.get('user')
1470 newdate = opts.get('date')
1470 newdate = opts.get('date')
1471 if newdate:
1471 if newdate:
1472 newdate = '%d %d' % util.parsedate(newdate)
1472 newdate = '%d %d' % util.parsedate(newdate)
1473 wlock = repo.wlock()
1473 wlock = repo.wlock()
1474
1474
1475 try:
1475 try:
1476 self.checktoppatch(repo)
1476 self.checktoppatch(repo)
1477 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1477 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1478 if repo.changelog.heads(top) != [top]:
1478 if repo.changelog.heads(top) != [top]:
1479 raise util.Abort(_("cannot refresh a revision with children"))
1479 raise util.Abort(_("cannot refresh a revision with children"))
1480 if not repo[top].mutable():
1480 if not repo[top].mutable():
1481 raise util.Abort(_("cannot refresh immutable revision"),
1481 raise util.Abort(_("cannot refresh immutable revision"),
1482 hint=_('see "hg help phases" for details'))
1482 hint=_('see "hg help phases" for details'))
1483
1483
1484 inclsubs = self.checksubstate(repo)
1484 inclsubs = self.checksubstate(repo)
1485
1485
1486 cparents = repo.changelog.parents(top)
1486 cparents = repo.changelog.parents(top)
1487 patchparent = self.qparents(repo, top)
1487 patchparent = self.qparents(repo, top)
1488 ph = patchheader(self.join(patchfn), self.plainmode)
1488 ph = patchheader(self.join(patchfn), self.plainmode)
1489 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1489 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1490 if msg:
1490 if msg:
1491 ph.setmessage(msg)
1491 ph.setmessage(msg)
1492 if newuser:
1492 if newuser:
1493 ph.setuser(newuser)
1493 ph.setuser(newuser)
1494 if newdate:
1494 if newdate:
1495 ph.setdate(newdate)
1495 ph.setdate(newdate)
1496 ph.setparent(hex(patchparent))
1496 ph.setparent(hex(patchparent))
1497
1497
1498 # only commit new patch when write is complete
1498 # only commit new patch when write is complete
1499 patchf = self.opener(patchfn, 'w', atomictemp=True)
1499 patchf = self.opener(patchfn, 'w', atomictemp=True)
1500
1500
1501 comments = str(ph)
1501 comments = str(ph)
1502 if comments:
1502 if comments:
1503 patchf.write(comments)
1503 patchf.write(comments)
1504
1504
1505 # update the dirstate in place, strip off the qtip commit
1505 # update the dirstate in place, strip off the qtip commit
1506 # and then commit.
1506 # and then commit.
1507 #
1507 #
1508 # this should really read:
1508 # this should really read:
1509 # mm, dd, aa = repo.status(top, patchparent)[:3]
1509 # mm, dd, aa = repo.status(top, patchparent)[:3]
1510 # but we do it backwards to take advantage of manifest/chlog
1510 # but we do it backwards to take advantage of manifest/chlog
1511 # caching against the next repo.status call
1511 # caching against the next repo.status call
1512 mm, aa, dd = repo.status(patchparent, top)[:3]
1512 mm, aa, dd = repo.status(patchparent, top)[:3]
1513 changes = repo.changelog.read(top)
1513 changes = repo.changelog.read(top)
1514 man = repo.manifest.read(changes[0])
1514 man = repo.manifest.read(changes[0])
1515 aaa = aa[:]
1515 aaa = aa[:]
1516 matchfn = scmutil.match(repo[None], pats, opts)
1516 matchfn = scmutil.match(repo[None], pats, opts)
1517 # in short mode, we only diff the files included in the
1517 # in short mode, we only diff the files included in the
1518 # patch already plus specified files
1518 # patch already plus specified files
1519 if opts.get('short'):
1519 if opts.get('short'):
1520 # if amending a patch, we start with existing
1520 # if amending a patch, we start with existing
1521 # files plus specified files - unfiltered
1521 # files plus specified files - unfiltered
1522 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1522 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1523 # filter with inc/exl options
1523 # filter with inc/exl options
1524 matchfn = scmutil.match(repo[None], opts=opts)
1524 matchfn = scmutil.match(repo[None], opts=opts)
1525 else:
1525 else:
1526 match = scmutil.matchall(repo)
1526 match = scmutil.matchall(repo)
1527 m, a, r, d = repo.status(match=match)[:4]
1527 m, a, r, d = repo.status(match=match)[:4]
1528 mm = set(mm)
1528 mm = set(mm)
1529 aa = set(aa)
1529 aa = set(aa)
1530 dd = set(dd)
1530 dd = set(dd)
1531
1531
1532 # we might end up with files that were added between
1532 # we might end up with files that were added between
1533 # qtip and the dirstate parent, but then changed in the
1533 # qtip and the dirstate parent, but then changed in the
1534 # local dirstate. in this case, we want them to only
1534 # local dirstate. in this case, we want them to only
1535 # show up in the added section
1535 # show up in the added section
1536 for x in m:
1536 for x in m:
1537 if x not in aa:
1537 if x not in aa:
1538 mm.add(x)
1538 mm.add(x)
1539 # we might end up with files added by the local dirstate that
1539 # we might end up with files added by the local dirstate that
1540 # were deleted by the patch. In this case, they should only
1540 # were deleted by the patch. In this case, they should only
1541 # show up in the changed section.
1541 # show up in the changed section.
1542 for x in a:
1542 for x in a:
1543 if x in dd:
1543 if x in dd:
1544 dd.remove(x)
1544 dd.remove(x)
1545 mm.add(x)
1545 mm.add(x)
1546 else:
1546 else:
1547 aa.add(x)
1547 aa.add(x)
1548 # make sure any files deleted in the local dirstate
1548 # make sure any files deleted in the local dirstate
1549 # are not in the add or change column of the patch
1549 # are not in the add or change column of the patch
1550 forget = []
1550 forget = []
1551 for x in d + r:
1551 for x in d + r:
1552 if x in aa:
1552 if x in aa:
1553 aa.remove(x)
1553 aa.remove(x)
1554 forget.append(x)
1554 forget.append(x)
1555 continue
1555 continue
1556 else:
1556 else:
1557 mm.discard(x)
1557 mm.discard(x)
1558 dd.add(x)
1558 dd.add(x)
1559
1559
1560 m = list(mm)
1560 m = list(mm)
1561 r = list(dd)
1561 r = list(dd)
1562 a = list(aa)
1562 a = list(aa)
1563 c = [filter(matchfn, l) for l in (m, a, r)]
1563 c = [filter(matchfn, l) for l in (m, a, r)]
1564 match = scmutil.matchfiles(repo, set(c[0] + c[1] + c[2] + inclsubs))
1564 match = scmutil.matchfiles(repo, set(c[0] + c[1] + c[2] + inclsubs))
1565 chunks = patchmod.diff(repo, patchparent, match=match,
1565 chunks = patchmod.diff(repo, patchparent, match=match,
1566 changes=c, opts=diffopts)
1566 changes=c, opts=diffopts)
1567 for chunk in chunks:
1567 for chunk in chunks:
1568 patchf.write(chunk)
1568 patchf.write(chunk)
1569
1569
1570 try:
1570 try:
1571 if diffopts.git or diffopts.upgrade:
1571 if diffopts.git or diffopts.upgrade:
1572 copies = {}
1572 copies = {}
1573 for dst in a:
1573 for dst in a:
1574 src = repo.dirstate.copied(dst)
1574 src = repo.dirstate.copied(dst)
1575 # during qfold, the source file for copies may
1575 # during qfold, the source file for copies may
1576 # be removed. Treat this as a simple add.
1576 # be removed. Treat this as a simple add.
1577 if src is not None and src in repo.dirstate:
1577 if src is not None and src in repo.dirstate:
1578 copies.setdefault(src, []).append(dst)
1578 copies.setdefault(src, []).append(dst)
1579 repo.dirstate.add(dst)
1579 repo.dirstate.add(dst)
1580 # remember the copies between patchparent and qtip
1580 # remember the copies between patchparent and qtip
1581 for dst in aaa:
1581 for dst in aaa:
1582 f = repo.file(dst)
1582 f = repo.file(dst)
1583 src = f.renamed(man[dst])
1583 src = f.renamed(man[dst])
1584 if src:
1584 if src:
1585 copies.setdefault(src[0], []).extend(
1585 copies.setdefault(src[0], []).extend(
1586 copies.get(dst, []))
1586 copies.get(dst, []))
1587 if dst in a:
1587 if dst in a:
1588 copies[src[0]].append(dst)
1588 copies[src[0]].append(dst)
1589 # we can't copy a file created by the patch itself
1589 # we can't copy a file created by the patch itself
1590 if dst in copies:
1590 if dst in copies:
1591 del copies[dst]
1591 del copies[dst]
1592 for src, dsts in copies.iteritems():
1592 for src, dsts in copies.iteritems():
1593 for dst in dsts:
1593 for dst in dsts:
1594 repo.dirstate.copy(src, dst)
1594 repo.dirstate.copy(src, dst)
1595 else:
1595 else:
1596 for dst in a:
1596 for dst in a:
1597 repo.dirstate.add(dst)
1597 repo.dirstate.add(dst)
1598 # Drop useless copy information
1598 # Drop useless copy information
1599 for f in list(repo.dirstate.copies()):
1599 for f in list(repo.dirstate.copies()):
1600 repo.dirstate.copy(None, f)
1600 repo.dirstate.copy(None, f)
1601 for f in r:
1601 for f in r:
1602 repo.dirstate.remove(f)
1602 repo.dirstate.remove(f)
1603 # if the patch excludes a modified file, mark that
1603 # if the patch excludes a modified file, mark that
1604 # file with mtime=0 so status can see it.
1604 # file with mtime=0 so status can see it.
1605 mm = []
1605 mm = []
1606 for i in xrange(len(m)-1, -1, -1):
1606 for i in xrange(len(m)-1, -1, -1):
1607 if not matchfn(m[i]):
1607 if not matchfn(m[i]):
1608 mm.append(m[i])
1608 mm.append(m[i])
1609 del m[i]
1609 del m[i]
1610 for f in m:
1610 for f in m:
1611 repo.dirstate.normal(f)
1611 repo.dirstate.normal(f)
1612 for f in mm:
1612 for f in mm:
1613 repo.dirstate.normallookup(f)
1613 repo.dirstate.normallookup(f)
1614 for f in forget:
1614 for f in forget:
1615 repo.dirstate.drop(f)
1615 repo.dirstate.drop(f)
1616
1616
1617 if not msg:
1617 if not msg:
1618 if not ph.message:
1618 if not ph.message:
1619 message = "[mq]: %s\n" % patchfn
1619 message = "[mq]: %s\n" % patchfn
1620 else:
1620 else:
1621 message = "\n".join(ph.message)
1621 message = "\n".join(ph.message)
1622 else:
1622 else:
1623 message = msg
1623 message = msg
1624
1624
1625 user = ph.user or changes[1]
1625 user = ph.user or changes[1]
1626
1626
1627 oldphase = repo[top].phase()
1627 oldphase = repo[top].phase()
1628
1628
1629 # assumes strip can roll itself back if interrupted
1629 # assumes strip can roll itself back if interrupted
1630 repo.setparents(*cparents)
1630 repo.setparents(*cparents)
1631 self.applied.pop()
1631 self.applied.pop()
1632 self.applieddirty = True
1632 self.applieddirty = True
1633 self.strip(repo, [top], update=False,
1633 self.strip(repo, [top], update=False,
1634 backup='strip')
1634 backup='strip')
1635 except: # re-raises
1635 except: # re-raises
1636 repo.dirstate.invalidate()
1636 repo.dirstate.invalidate()
1637 raise
1637 raise
1638
1638
1639 try:
1639 try:
1640 # might be nice to attempt to roll back strip after this
1640 # might be nice to attempt to roll back strip after this
1641
1641
1642 # Ensure we create a new changeset in the same phase than
1642 # Ensure we create a new changeset in the same phase than
1643 # the old one.
1643 # the old one.
1644 n = newcommit(repo, oldphase, message, user, ph.date,
1644 n = newcommit(repo, oldphase, message, user, ph.date,
1645 match=match, force=True)
1645 match=match, force=True)
1646 # only write patch after a successful commit
1646 # only write patch after a successful commit
1647 patchf.close()
1647 patchf.close()
1648 self.applied.append(statusentry(n, patchfn))
1648 self.applied.append(statusentry(n, patchfn))
1649 except: # re-raises
1649 except: # re-raises
1650 ctx = repo[cparents[0]]
1650 ctx = repo[cparents[0]]
1651 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1651 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1652 self.savedirty()
1652 self.savedirty()
1653 self.ui.warn(_('refresh interrupted while patch was popped! '
1653 self.ui.warn(_('refresh interrupted while patch was popped! '
1654 '(revert --all, qpush to recover)\n'))
1654 '(revert --all, qpush to recover)\n'))
1655 raise
1655 raise
1656 finally:
1656 finally:
1657 wlock.release()
1657 wlock.release()
1658 self.removeundo(repo)
1658 self.removeundo(repo)
1659
1659
1660 def init(self, repo, create=False):
1660 def init(self, repo, create=False):
1661 if not create and os.path.isdir(self.path):
1661 if not create and os.path.isdir(self.path):
1662 raise util.Abort(_("patch queue directory already exists"))
1662 raise util.Abort(_("patch queue directory already exists"))
1663 try:
1663 try:
1664 os.mkdir(self.path)
1664 os.mkdir(self.path)
1665 except OSError, inst:
1665 except OSError, inst:
1666 if inst.errno != errno.EEXIST or not create:
1666 if inst.errno != errno.EEXIST or not create:
1667 raise
1667 raise
1668 if create:
1668 if create:
1669 return self.qrepo(create=True)
1669 return self.qrepo(create=True)
1670
1670
1671 def unapplied(self, repo, patch=None):
1671 def unapplied(self, repo, patch=None):
1672 if patch and patch not in self.series:
1672 if patch and patch not in self.series:
1673 raise util.Abort(_("patch %s is not in series file") % patch)
1673 raise util.Abort(_("patch %s is not in series file") % patch)
1674 if not patch:
1674 if not patch:
1675 start = self.seriesend()
1675 start = self.seriesend()
1676 else:
1676 else:
1677 start = self.series.index(patch) + 1
1677 start = self.series.index(patch) + 1
1678 unapplied = []
1678 unapplied = []
1679 for i in xrange(start, len(self.series)):
1679 for i in xrange(start, len(self.series)):
1680 pushable, reason = self.pushable(i)
1680 pushable, reason = self.pushable(i)
1681 if pushable:
1681 if pushable:
1682 unapplied.append((i, self.series[i]))
1682 unapplied.append((i, self.series[i]))
1683 self.explainpushable(i)
1683 self.explainpushable(i)
1684 return unapplied
1684 return unapplied
1685
1685
1686 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1686 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1687 summary=False):
1687 summary=False):
1688 def displayname(pfx, patchname, state):
1688 def displayname(pfx, patchname, state):
1689 if pfx:
1689 if pfx:
1690 self.ui.write(pfx)
1690 self.ui.write(pfx)
1691 if summary:
1691 if summary:
1692 ph = patchheader(self.join(patchname), self.plainmode)
1692 ph = patchheader(self.join(patchname), self.plainmode)
1693 msg = ph.message and ph.message[0] or ''
1693 msg = ph.message and ph.message[0] or ''
1694 if self.ui.formatted():
1694 if self.ui.formatted():
1695 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1695 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1696 if width > 0:
1696 if width > 0:
1697 msg = util.ellipsis(msg, width)
1697 msg = util.ellipsis(msg, width)
1698 else:
1698 else:
1699 msg = ''
1699 msg = ''
1700 self.ui.write(patchname, label='qseries.' + state)
1700 self.ui.write(patchname, label='qseries.' + state)
1701 self.ui.write(': ')
1701 self.ui.write(': ')
1702 self.ui.write(msg, label='qseries.message.' + state)
1702 self.ui.write(msg, label='qseries.message.' + state)
1703 else:
1703 else:
1704 self.ui.write(patchname, label='qseries.' + state)
1704 self.ui.write(patchname, label='qseries.' + state)
1705 self.ui.write('\n')
1705 self.ui.write('\n')
1706
1706
1707 applied = set([p.name for p in self.applied])
1707 applied = set([p.name for p in self.applied])
1708 if length is None:
1708 if length is None:
1709 length = len(self.series) - start
1709 length = len(self.series) - start
1710 if not missing:
1710 if not missing:
1711 if self.ui.verbose:
1711 if self.ui.verbose:
1712 idxwidth = len(str(start + length - 1))
1712 idxwidth = len(str(start + length - 1))
1713 for i in xrange(start, start + length):
1713 for i in xrange(start, start + length):
1714 patch = self.series[i]
1714 patch = self.series[i]
1715 if patch in applied:
1715 if patch in applied:
1716 char, state = 'A', 'applied'
1716 char, state = 'A', 'applied'
1717 elif self.pushable(i)[0]:
1717 elif self.pushable(i)[0]:
1718 char, state = 'U', 'unapplied'
1718 char, state = 'U', 'unapplied'
1719 else:
1719 else:
1720 char, state = 'G', 'guarded'
1720 char, state = 'G', 'guarded'
1721 pfx = ''
1721 pfx = ''
1722 if self.ui.verbose:
1722 if self.ui.verbose:
1723 pfx = '%*d %s ' % (idxwidth, i, char)
1723 pfx = '%*d %s ' % (idxwidth, i, char)
1724 elif status and status != char:
1724 elif status and status != char:
1725 continue
1725 continue
1726 displayname(pfx, patch, state)
1726 displayname(pfx, patch, state)
1727 else:
1727 else:
1728 msng_list = []
1728 msng_list = []
1729 for root, dirs, files in os.walk(self.path):
1729 for root, dirs, files in os.walk(self.path):
1730 d = root[len(self.path) + 1:]
1730 d = root[len(self.path) + 1:]
1731 for f in files:
1731 for f in files:
1732 fl = os.path.join(d, f)
1732 fl = os.path.join(d, f)
1733 if (fl not in self.series and
1733 if (fl not in self.series and
1734 fl not in (self.statuspath, self.seriespath,
1734 fl not in (self.statuspath, self.seriespath,
1735 self.guardspath)
1735 self.guardspath)
1736 and not fl.startswith('.')):
1736 and not fl.startswith('.')):
1737 msng_list.append(fl)
1737 msng_list.append(fl)
1738 for x in sorted(msng_list):
1738 for x in sorted(msng_list):
1739 pfx = self.ui.verbose and ('D ') or ''
1739 pfx = self.ui.verbose and ('D ') or ''
1740 displayname(pfx, x, 'missing')
1740 displayname(pfx, x, 'missing')
1741
1741
1742 def issaveline(self, l):
1742 def issaveline(self, l):
1743 if l.name == '.hg.patches.save.line':
1743 if l.name == '.hg.patches.save.line':
1744 return True
1744 return True
1745
1745
1746 def qrepo(self, create=False):
1746 def qrepo(self, create=False):
1747 ui = self.ui.copy()
1747 ui = self.ui.copy()
1748 ui.setconfig('paths', 'default', '', overlay=False)
1748 ui.setconfig('paths', 'default', '', overlay=False)
1749 ui.setconfig('paths', 'default-push', '', overlay=False)
1749 ui.setconfig('paths', 'default-push', '', overlay=False)
1750 if create or os.path.isdir(self.join(".hg")):
1750 if create or os.path.isdir(self.join(".hg")):
1751 return hg.repository(ui, path=self.path, create=create)
1751 return hg.repository(ui, path=self.path, create=create)
1752
1752
1753 def restore(self, repo, rev, delete=None, qupdate=None):
1753 def restore(self, repo, rev, delete=None, qupdate=None):
1754 desc = repo[rev].description().strip()
1754 desc = repo[rev].description().strip()
1755 lines = desc.splitlines()
1755 lines = desc.splitlines()
1756 i = 0
1756 i = 0
1757 datastart = None
1757 datastart = None
1758 series = []
1758 series = []
1759 applied = []
1759 applied = []
1760 qpp = None
1760 qpp = None
1761 for i, line in enumerate(lines):
1761 for i, line in enumerate(lines):
1762 if line == 'Patch Data:':
1762 if line == 'Patch Data:':
1763 datastart = i + 1
1763 datastart = i + 1
1764 elif line.startswith('Dirstate:'):
1764 elif line.startswith('Dirstate:'):
1765 l = line.rstrip()
1765 l = line.rstrip()
1766 l = l[10:].split(' ')
1766 l = l[10:].split(' ')
1767 qpp = [bin(x) for x in l]
1767 qpp = [bin(x) for x in l]
1768 elif datastart is not None:
1768 elif datastart is not None:
1769 l = line.rstrip()
1769 l = line.rstrip()
1770 n, name = l.split(':', 1)
1770 n, name = l.split(':', 1)
1771 if n:
1771 if n:
1772 applied.append(statusentry(bin(n), name))
1772 applied.append(statusentry(bin(n), name))
1773 else:
1773 else:
1774 series.append(l)
1774 series.append(l)
1775 if datastart is None:
1775 if datastart is None:
1776 self.ui.warn(_("no saved patch data found\n"))
1776 self.ui.warn(_("no saved patch data found\n"))
1777 return 1
1777 return 1
1778 self.ui.warn(_("restoring status: %s\n") % lines[0])
1778 self.ui.warn(_("restoring status: %s\n") % lines[0])
1779 self.fullseries = series
1779 self.fullseries = series
1780 self.applied = applied
1780 self.applied = applied
1781 self.parseseries()
1781 self.parseseries()
1782 self.seriesdirty = True
1782 self.seriesdirty = True
1783 self.applieddirty = True
1783 self.applieddirty = True
1784 heads = repo.changelog.heads()
1784 heads = repo.changelog.heads()
1785 if delete:
1785 if delete:
1786 if rev not in heads:
1786 if rev not in heads:
1787 self.ui.warn(_("save entry has children, leaving it alone\n"))
1787 self.ui.warn(_("save entry has children, leaving it alone\n"))
1788 else:
1788 else:
1789 self.ui.warn(_("removing save entry %s\n") % short(rev))
1789 self.ui.warn(_("removing save entry %s\n") % short(rev))
1790 pp = repo.dirstate.parents()
1790 pp = repo.dirstate.parents()
1791 if rev in pp:
1791 if rev in pp:
1792 update = True
1792 update = True
1793 else:
1793 else:
1794 update = False
1794 update = False
1795 self.strip(repo, [rev], update=update, backup='strip')
1795 self.strip(repo, [rev], update=update, backup='strip')
1796 if qpp:
1796 if qpp:
1797 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1797 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1798 (short(qpp[0]), short(qpp[1])))
1798 (short(qpp[0]), short(qpp[1])))
1799 if qupdate:
1799 if qupdate:
1800 self.ui.status(_("updating queue directory\n"))
1800 self.ui.status(_("updating queue directory\n"))
1801 r = self.qrepo()
1801 r = self.qrepo()
1802 if not r:
1802 if not r:
1803 self.ui.warn(_("unable to load queue repository\n"))
1803 self.ui.warn(_("unable to load queue repository\n"))
1804 return 1
1804 return 1
1805 hg.clean(r, qpp[0])
1805 hg.clean(r, qpp[0])
1806
1806
1807 def save(self, repo, msg=None):
1807 def save(self, repo, msg=None):
1808 if not self.applied:
1808 if not self.applied:
1809 self.ui.warn(_("save: no patches applied, exiting\n"))
1809 self.ui.warn(_("save: no patches applied, exiting\n"))
1810 return 1
1810 return 1
1811 if self.issaveline(self.applied[-1]):
1811 if self.issaveline(self.applied[-1]):
1812 self.ui.warn(_("status is already saved\n"))
1812 self.ui.warn(_("status is already saved\n"))
1813 return 1
1813 return 1
1814
1814
1815 if not msg:
1815 if not msg:
1816 msg = _("hg patches saved state")
1816 msg = _("hg patches saved state")
1817 else:
1817 else:
1818 msg = "hg patches: " + msg.rstrip('\r\n')
1818 msg = "hg patches: " + msg.rstrip('\r\n')
1819 r = self.qrepo()
1819 r = self.qrepo()
1820 if r:
1820 if r:
1821 pp = r.dirstate.parents()
1821 pp = r.dirstate.parents()
1822 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1822 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1823 msg += "\n\nPatch Data:\n"
1823 msg += "\n\nPatch Data:\n"
1824 msg += ''.join('%s\n' % x for x in self.applied)
1824 msg += ''.join('%s\n' % x for x in self.applied)
1825 msg += ''.join(':%s\n' % x for x in self.fullseries)
1825 msg += ''.join(':%s\n' % x for x in self.fullseries)
1826 n = repo.commit(msg, force=True)
1826 n = repo.commit(msg, force=True)
1827 if not n:
1827 if not n:
1828 self.ui.warn(_("repo commit failed\n"))
1828 self.ui.warn(_("repo commit failed\n"))
1829 return 1
1829 return 1
1830 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1830 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1831 self.applieddirty = True
1831 self.applieddirty = True
1832 self.removeundo(repo)
1832 self.removeundo(repo)
1833
1833
1834 def fullseriesend(self):
1834 def fullseriesend(self):
1835 if self.applied:
1835 if self.applied:
1836 p = self.applied[-1].name
1836 p = self.applied[-1].name
1837 end = self.findseries(p)
1837 end = self.findseries(p)
1838 if end is None:
1838 if end is None:
1839 return len(self.fullseries)
1839 return len(self.fullseries)
1840 return end + 1
1840 return end + 1
1841 return 0
1841 return 0
1842
1842
1843 def seriesend(self, all_patches=False):
1843 def seriesend(self, all_patches=False):
1844 """If all_patches is False, return the index of the next pushable patch
1844 """If all_patches is False, return the index of the next pushable patch
1845 in the series, or the series length. If all_patches is True, return the
1845 in the series, or the series length. If all_patches is True, return the
1846 index of the first patch past the last applied one.
1846 index of the first patch past the last applied one.
1847 """
1847 """
1848 end = 0
1848 end = 0
1849 def next(start):
1849 def next(start):
1850 if all_patches or start >= len(self.series):
1850 if all_patches or start >= len(self.series):
1851 return start
1851 return start
1852 for i in xrange(start, len(self.series)):
1852 for i in xrange(start, len(self.series)):
1853 p, reason = self.pushable(i)
1853 p, reason = self.pushable(i)
1854 if p:
1854 if p:
1855 return i
1855 return i
1856 self.explainpushable(i)
1856 self.explainpushable(i)
1857 return len(self.series)
1857 return len(self.series)
1858 if self.applied:
1858 if self.applied:
1859 p = self.applied[-1].name
1859 p = self.applied[-1].name
1860 try:
1860 try:
1861 end = self.series.index(p)
1861 end = self.series.index(p)
1862 except ValueError:
1862 except ValueError:
1863 return 0
1863 return 0
1864 return next(end + 1)
1864 return next(end + 1)
1865 return next(end)
1865 return next(end)
1866
1866
1867 def appliedname(self, index):
1867 def appliedname(self, index):
1868 pname = self.applied[index].name
1868 pname = self.applied[index].name
1869 if not self.ui.verbose:
1869 if not self.ui.verbose:
1870 p = pname
1870 p = pname
1871 else:
1871 else:
1872 p = str(self.series.index(pname)) + " " + pname
1872 p = str(self.series.index(pname)) + " " + pname
1873 return p
1873 return p
1874
1874
1875 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1875 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1876 force=None, git=False):
1876 force=None, git=False):
1877 def checkseries(patchname):
1877 def checkseries(patchname):
1878 if patchname in self.series:
1878 if patchname in self.series:
1879 raise util.Abort(_('patch %s is already in the series file')
1879 raise util.Abort(_('patch %s is already in the series file')
1880 % patchname)
1880 % patchname)
1881
1881
1882 if rev:
1882 if rev:
1883 if files:
1883 if files:
1884 raise util.Abort(_('option "-r" not valid when importing '
1884 raise util.Abort(_('option "-r" not valid when importing '
1885 'files'))
1885 'files'))
1886 rev = scmutil.revrange(repo, rev)
1886 rev = scmutil.revrange(repo, rev)
1887 rev.sort(reverse=True)
1887 rev.sort(reverse=True)
1888 elif not files:
1888 elif not files:
1889 raise util.Abort(_('no files or revisions specified'))
1889 raise util.Abort(_('no files or revisions specified'))
1890 if (len(files) > 1 or len(rev) > 1) and patchname:
1890 if (len(files) > 1 or len(rev) > 1) and patchname:
1891 raise util.Abort(_('option "-n" not valid when importing multiple '
1891 raise util.Abort(_('option "-n" not valid when importing multiple '
1892 'patches'))
1892 'patches'))
1893 imported = []
1893 imported = []
1894 if rev:
1894 if rev:
1895 # If mq patches are applied, we can only import revisions
1895 # If mq patches are applied, we can only import revisions
1896 # that form a linear path to qbase.
1896 # that form a linear path to qbase.
1897 # Otherwise, they should form a linear path to a head.
1897 # Otherwise, they should form a linear path to a head.
1898 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1898 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1899 if len(heads) > 1:
1899 if len(heads) > 1:
1900 raise util.Abort(_('revision %d is the root of more than one '
1900 raise util.Abort(_('revision %d is the root of more than one '
1901 'branch') % rev[-1])
1901 'branch') % rev[-1])
1902 if self.applied:
1902 if self.applied:
1903 base = repo.changelog.node(rev[0])
1903 base = repo.changelog.node(rev[0])
1904 if base in [n.node for n in self.applied]:
1904 if base in [n.node for n in self.applied]:
1905 raise util.Abort(_('revision %d is already managed')
1905 raise util.Abort(_('revision %d is already managed')
1906 % rev[0])
1906 % rev[0])
1907 if heads != [self.applied[-1].node]:
1907 if heads != [self.applied[-1].node]:
1908 raise util.Abort(_('revision %d is not the parent of '
1908 raise util.Abort(_('revision %d is not the parent of '
1909 'the queue') % rev[0])
1909 'the queue') % rev[0])
1910 base = repo.changelog.rev(self.applied[0].node)
1910 base = repo.changelog.rev(self.applied[0].node)
1911 lastparent = repo.changelog.parentrevs(base)[0]
1911 lastparent = repo.changelog.parentrevs(base)[0]
1912 else:
1912 else:
1913 if heads != [repo.changelog.node(rev[0])]:
1913 if heads != [repo.changelog.node(rev[0])]:
1914 raise util.Abort(_('revision %d has unmanaged children')
1914 raise util.Abort(_('revision %d has unmanaged children')
1915 % rev[0])
1915 % rev[0])
1916 lastparent = None
1916 lastparent = None
1917
1917
1918 diffopts = self.diffopts({'git': git})
1918 diffopts = self.diffopts({'git': git})
1919 for r in rev:
1919 for r in rev:
1920 if not repo[r].mutable():
1920 if not repo[r].mutable():
1921 raise util.Abort(_('revision %d is not mutable') % r,
1921 raise util.Abort(_('revision %d is not mutable') % r,
1922 hint=_('see "hg help phases" for details'))
1922 hint=_('see "hg help phases" for details'))
1923 p1, p2 = repo.changelog.parentrevs(r)
1923 p1, p2 = repo.changelog.parentrevs(r)
1924 n = repo.changelog.node(r)
1924 n = repo.changelog.node(r)
1925 if p2 != nullrev:
1925 if p2 != nullrev:
1926 raise util.Abort(_('cannot import merge revision %d') % r)
1926 raise util.Abort(_('cannot import merge revision %d') % r)
1927 if lastparent and lastparent != r:
1927 if lastparent and lastparent != r:
1928 raise util.Abort(_('revision %d is not the parent of %d')
1928 raise util.Abort(_('revision %d is not the parent of %d')
1929 % (r, lastparent))
1929 % (r, lastparent))
1930 lastparent = p1
1930 lastparent = p1
1931
1931
1932 if not patchname:
1932 if not patchname:
1933 patchname = normname('%d.diff' % r)
1933 patchname = normname('%d.diff' % r)
1934 checkseries(patchname)
1934 checkseries(patchname)
1935 self.checkpatchname(patchname, force)
1935 self.checkpatchname(patchname, force)
1936 self.fullseries.insert(0, patchname)
1936 self.fullseries.insert(0, patchname)
1937
1937
1938 patchf = self.opener(patchname, "w")
1938 patchf = self.opener(patchname, "w")
1939 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1939 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1940 patchf.close()
1940 patchf.close()
1941
1941
1942 se = statusentry(n, patchname)
1942 se = statusentry(n, patchname)
1943 self.applied.insert(0, se)
1943 self.applied.insert(0, se)
1944
1944
1945 self.added.append(patchname)
1945 self.added.append(patchname)
1946 imported.append(patchname)
1946 imported.append(patchname)
1947 patchname = None
1947 patchname = None
1948 if rev and repo.ui.configbool('mq', 'secret', False):
1948 if rev and repo.ui.configbool('mq', 'secret', False):
1949 # if we added anything with --rev, we must move the secret root
1949 # if we added anything with --rev, we must move the secret root
1950 phases.retractboundary(repo, phases.secret, [n])
1950 phases.retractboundary(repo, phases.secret, [n])
1951 self.parseseries()
1951 self.parseseries()
1952 self.applieddirty = True
1952 self.applieddirty = True
1953 self.seriesdirty = True
1953 self.seriesdirty = True
1954
1954
1955 for i, filename in enumerate(files):
1955 for i, filename in enumerate(files):
1956 if existing:
1956 if existing:
1957 if filename == '-':
1957 if filename == '-':
1958 raise util.Abort(_('-e is incompatible with import from -'))
1958 raise util.Abort(_('-e is incompatible with import from -'))
1959 filename = normname(filename)
1959 filename = normname(filename)
1960 self.checkreservedname(filename)
1960 self.checkreservedname(filename)
1961 originpath = self.join(filename)
1961 originpath = self.join(filename)
1962 if not os.path.isfile(originpath):
1962 if not os.path.isfile(originpath):
1963 raise util.Abort(_("patch %s does not exist") % filename)
1963 raise util.Abort(_("patch %s does not exist") % filename)
1964
1964
1965 if patchname:
1965 if patchname:
1966 self.checkpatchname(patchname, force)
1966 self.checkpatchname(patchname, force)
1967
1967
1968 self.ui.write(_('renaming %s to %s\n')
1968 self.ui.write(_('renaming %s to %s\n')
1969 % (filename, patchname))
1969 % (filename, patchname))
1970 util.rename(originpath, self.join(patchname))
1970 util.rename(originpath, self.join(patchname))
1971 else:
1971 else:
1972 patchname = filename
1972 patchname = filename
1973
1973
1974 else:
1974 else:
1975 if filename == '-' and not patchname:
1975 if filename == '-' and not patchname:
1976 raise util.Abort(_('need --name to import a patch from -'))
1976 raise util.Abort(_('need --name to import a patch from -'))
1977 elif not patchname:
1977 elif not patchname:
1978 patchname = normname(os.path.basename(filename.rstrip('/')))
1978 patchname = normname(os.path.basename(filename.rstrip('/')))
1979 self.checkpatchname(patchname, force)
1979 self.checkpatchname(patchname, force)
1980 try:
1980 try:
1981 if filename == '-':
1981 if filename == '-':
1982 text = self.ui.fin.read()
1982 text = self.ui.fin.read()
1983 else:
1983 else:
1984 fp = url.open(self.ui, filename)
1984 fp = url.open(self.ui, filename)
1985 text = fp.read()
1985 text = fp.read()
1986 fp.close()
1986 fp.close()
1987 except (OSError, IOError):
1987 except (OSError, IOError):
1988 raise util.Abort(_("unable to read file %s") % filename)
1988 raise util.Abort(_("unable to read file %s") % filename)
1989 patchf = self.opener(patchname, "w")
1989 patchf = self.opener(patchname, "w")
1990 patchf.write(text)
1990 patchf.write(text)
1991 patchf.close()
1991 patchf.close()
1992 if not force:
1992 if not force:
1993 checkseries(patchname)
1993 checkseries(patchname)
1994 if patchname not in self.series:
1994 if patchname not in self.series:
1995 index = self.fullseriesend() + i
1995 index = self.fullseriesend() + i
1996 self.fullseries[index:index] = [patchname]
1996 self.fullseries[index:index] = [patchname]
1997 self.parseseries()
1997 self.parseseries()
1998 self.seriesdirty = True
1998 self.seriesdirty = True
1999 self.ui.warn(_("adding %s to series file\n") % patchname)
1999 self.ui.warn(_("adding %s to series file\n") % patchname)
2000 self.added.append(patchname)
2000 self.added.append(patchname)
2001 imported.append(patchname)
2001 imported.append(patchname)
2002 patchname = None
2002 patchname = None
2003
2003
2004 self.removeundo(repo)
2004 self.removeundo(repo)
2005 return imported
2005 return imported
2006
2006
2007 def fixkeepchangesopts(ui, opts):
2007 def fixkeepchangesopts(ui, opts):
2008 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2008 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2009 or opts.get('exact')):
2009 or opts.get('exact')):
2010 return opts
2010 return opts
2011 opts = dict(opts)
2011 opts = dict(opts)
2012 opts['keep_changes'] = True
2012 opts['keep_changes'] = True
2013 return opts
2013 return opts
2014
2014
2015 @command("qdelete|qremove|qrm",
2015 @command("qdelete|qremove|qrm",
2016 [('k', 'keep', None, _('keep patch file')),
2016 [('k', 'keep', None, _('keep patch file')),
2017 ('r', 'rev', [],
2017 ('r', 'rev', [],
2018 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2018 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2019 _('hg qdelete [-k] [PATCH]...'))
2019 _('hg qdelete [-k] [PATCH]...'))
2020 def delete(ui, repo, *patches, **opts):
2020 def delete(ui, repo, *patches, **opts):
2021 """remove patches from queue
2021 """remove patches from queue
2022
2022
2023 The patches must not be applied, and at least one patch is required. Exact
2023 The patches must not be applied, and at least one patch is required. Exact
2024 patch identifiers must be given. With -k/--keep, the patch files are
2024 patch identifiers must be given. With -k/--keep, the patch files are
2025 preserved in the patch directory.
2025 preserved in the patch directory.
2026
2026
2027 To stop managing a patch and move it into permanent history,
2027 To stop managing a patch and move it into permanent history,
2028 use the :hg:`qfinish` command."""
2028 use the :hg:`qfinish` command."""
2029 q = repo.mq
2029 q = repo.mq
2030 q.delete(repo, patches, opts)
2030 q.delete(repo, patches, opts)
2031 q.savedirty()
2031 q.savedirty()
2032 return 0
2032 return 0
2033
2033
2034 @command("qapplied",
2034 @command("qapplied",
2035 [('1', 'last', None, _('show only the preceding applied patch'))
2035 [('1', 'last', None, _('show only the preceding applied patch'))
2036 ] + seriesopts,
2036 ] + seriesopts,
2037 _('hg qapplied [-1] [-s] [PATCH]'))
2037 _('hg qapplied [-1] [-s] [PATCH]'))
2038 def applied(ui, repo, patch=None, **opts):
2038 def applied(ui, repo, patch=None, **opts):
2039 """print the patches already applied
2039 """print the patches already applied
2040
2040
2041 Returns 0 on success."""
2041 Returns 0 on success."""
2042
2042
2043 q = repo.mq
2043 q = repo.mq
2044
2044
2045 if patch:
2045 if patch:
2046 if patch not in q.series:
2046 if patch not in q.series:
2047 raise util.Abort(_("patch %s is not in series file") % patch)
2047 raise util.Abort(_("patch %s is not in series file") % patch)
2048 end = q.series.index(patch) + 1
2048 end = q.series.index(patch) + 1
2049 else:
2049 else:
2050 end = q.seriesend(True)
2050 end = q.seriesend(True)
2051
2051
2052 if opts.get('last') and not end:
2052 if opts.get('last') and not end:
2053 ui.write(_("no patches applied\n"))
2053 ui.write(_("no patches applied\n"))
2054 return 1
2054 return 1
2055 elif opts.get('last') and end == 1:
2055 elif opts.get('last') and end == 1:
2056 ui.write(_("only one patch applied\n"))
2056 ui.write(_("only one patch applied\n"))
2057 return 1
2057 return 1
2058 elif opts.get('last'):
2058 elif opts.get('last'):
2059 start = end - 2
2059 start = end - 2
2060 end = 1
2060 end = 1
2061 else:
2061 else:
2062 start = 0
2062 start = 0
2063
2063
2064 q.qseries(repo, length=end, start=start, status='A',
2064 q.qseries(repo, length=end, start=start, status='A',
2065 summary=opts.get('summary'))
2065 summary=opts.get('summary'))
2066
2066
2067
2067
2068 @command("qunapplied",
2068 @command("qunapplied",
2069 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2069 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2070 _('hg qunapplied [-1] [-s] [PATCH]'))
2070 _('hg qunapplied [-1] [-s] [PATCH]'))
2071 def unapplied(ui, repo, patch=None, **opts):
2071 def unapplied(ui, repo, patch=None, **opts):
2072 """print the patches not yet applied
2072 """print the patches not yet applied
2073
2073
2074 Returns 0 on success."""
2074 Returns 0 on success."""
2075
2075
2076 q = repo.mq
2076 q = repo.mq
2077 if patch:
2077 if patch:
2078 if patch not in q.series:
2078 if patch not in q.series:
2079 raise util.Abort(_("patch %s is not in series file") % patch)
2079 raise util.Abort(_("patch %s is not in series file") % patch)
2080 start = q.series.index(patch) + 1
2080 start = q.series.index(patch) + 1
2081 else:
2081 else:
2082 start = q.seriesend(True)
2082 start = q.seriesend(True)
2083
2083
2084 if start == len(q.series) and opts.get('first'):
2084 if start == len(q.series) and opts.get('first'):
2085 ui.write(_("all patches applied\n"))
2085 ui.write(_("all patches applied\n"))
2086 return 1
2086 return 1
2087
2087
2088 length = opts.get('first') and 1 or None
2088 length = opts.get('first') and 1 or None
2089 q.qseries(repo, start=start, length=length, status='U',
2089 q.qseries(repo, start=start, length=length, status='U',
2090 summary=opts.get('summary'))
2090 summary=opts.get('summary'))
2091
2091
2092 @command("qimport",
2092 @command("qimport",
2093 [('e', 'existing', None, _('import file in patch directory')),
2093 [('e', 'existing', None, _('import file in patch directory')),
2094 ('n', 'name', '',
2094 ('n', 'name', '',
2095 _('name of patch file'), _('NAME')),
2095 _('name of patch file'), _('NAME')),
2096 ('f', 'force', None, _('overwrite existing files')),
2096 ('f', 'force', None, _('overwrite existing files')),
2097 ('r', 'rev', [],
2097 ('r', 'rev', [],
2098 _('place existing revisions under mq control'), _('REV')),
2098 _('place existing revisions under mq control'), _('REV')),
2099 ('g', 'git', None, _('use git extended diff format')),
2099 ('g', 'git', None, _('use git extended diff format')),
2100 ('P', 'push', None, _('qpush after importing'))],
2100 ('P', 'push', None, _('qpush after importing'))],
2101 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2101 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2102 def qimport(ui, repo, *filename, **opts):
2102 def qimport(ui, repo, *filename, **opts):
2103 """import a patch or existing changeset
2103 """import a patch or existing changeset
2104
2104
2105 The patch is inserted into the series after the last applied
2105 The patch is inserted into the series after the last applied
2106 patch. If no patches have been applied, qimport prepends the patch
2106 patch. If no patches have been applied, qimport prepends the patch
2107 to the series.
2107 to the series.
2108
2108
2109 The patch will have the same name as its source file unless you
2109 The patch will have the same name as its source file unless you
2110 give it a new one with -n/--name.
2110 give it a new one with -n/--name.
2111
2111
2112 You can register an existing patch inside the patch directory with
2112 You can register an existing patch inside the patch directory with
2113 the -e/--existing flag.
2113 the -e/--existing flag.
2114
2114
2115 With -f/--force, an existing patch of the same name will be
2115 With -f/--force, an existing patch of the same name will be
2116 overwritten.
2116 overwritten.
2117
2117
2118 An existing changeset may be placed under mq control with -r/--rev
2118 An existing changeset may be placed under mq control with -r/--rev
2119 (e.g. qimport --rev tip -n patch will place tip under mq control).
2119 (e.g. qimport --rev tip -n patch will place tip under mq control).
2120 With -g/--git, patches imported with --rev will use the git diff
2120 With -g/--git, patches imported with --rev will use the git diff
2121 format. See the diffs help topic for information on why this is
2121 format. See the diffs help topic for information on why this is
2122 important for preserving rename/copy information and permission
2122 important for preserving rename/copy information and permission
2123 changes. Use :hg:`qfinish` to remove changesets from mq control.
2123 changes. Use :hg:`qfinish` to remove changesets from mq control.
2124
2124
2125 To import a patch from standard input, pass - as the patch file.
2125 To import a patch from standard input, pass - as the patch file.
2126 When importing from standard input, a patch name must be specified
2126 When importing from standard input, a patch name must be specified
2127 using the --name flag.
2127 using the --name flag.
2128
2128
2129 To import an existing patch while renaming it::
2129 To import an existing patch while renaming it::
2130
2130
2131 hg qimport -e existing-patch -n new-name
2131 hg qimport -e existing-patch -n new-name
2132
2132
2133 Returns 0 if import succeeded.
2133 Returns 0 if import succeeded.
2134 """
2134 """
2135 lock = repo.lock() # cause this may move phase
2135 lock = repo.lock() # cause this may move phase
2136 try:
2136 try:
2137 q = repo.mq
2137 q = repo.mq
2138 try:
2138 try:
2139 imported = q.qimport(
2139 imported = q.qimport(
2140 repo, filename, patchname=opts.get('name'),
2140 repo, filename, patchname=opts.get('name'),
2141 existing=opts.get('existing'), force=opts.get('force'),
2141 existing=opts.get('existing'), force=opts.get('force'),
2142 rev=opts.get('rev'), git=opts.get('git'))
2142 rev=opts.get('rev'), git=opts.get('git'))
2143 finally:
2143 finally:
2144 q.savedirty()
2144 q.savedirty()
2145 finally:
2145 finally:
2146 lock.release()
2146 lock.release()
2147
2147
2148 if imported and opts.get('push') and not opts.get('rev'):
2148 if imported and opts.get('push') and not opts.get('rev'):
2149 return q.push(repo, imported[-1])
2149 return q.push(repo, imported[-1])
2150 return 0
2150 return 0
2151
2151
2152 def qinit(ui, repo, create):
2152 def qinit(ui, repo, create):
2153 """initialize a new queue repository
2153 """initialize a new queue repository
2154
2154
2155 This command also creates a series file for ordering patches, and
2155 This command also creates a series file for ordering patches, and
2156 an mq-specific .hgignore file in the queue repository, to exclude
2156 an mq-specific .hgignore file in the queue repository, to exclude
2157 the status and guards files (these contain mostly transient state).
2157 the status and guards files (these contain mostly transient state).
2158
2158
2159 Returns 0 if initialization succeeded."""
2159 Returns 0 if initialization succeeded."""
2160 q = repo.mq
2160 q = repo.mq
2161 r = q.init(repo, create)
2161 r = q.init(repo, create)
2162 q.savedirty()
2162 q.savedirty()
2163 if r:
2163 if r:
2164 if not os.path.exists(r.wjoin('.hgignore')):
2164 if not os.path.exists(r.wjoin('.hgignore')):
2165 fp = r.wopener('.hgignore', 'w')
2165 fp = r.wopener('.hgignore', 'w')
2166 fp.write('^\\.hg\n')
2166 fp.write('^\\.hg\n')
2167 fp.write('^\\.mq\n')
2167 fp.write('^\\.mq\n')
2168 fp.write('syntax: glob\n')
2168 fp.write('syntax: glob\n')
2169 fp.write('status\n')
2169 fp.write('status\n')
2170 fp.write('guards\n')
2170 fp.write('guards\n')
2171 fp.close()
2171 fp.close()
2172 if not os.path.exists(r.wjoin('series')):
2172 if not os.path.exists(r.wjoin('series')):
2173 r.wopener('series', 'w').close()
2173 r.wopener('series', 'w').close()
2174 r[None].add(['.hgignore', 'series'])
2174 r[None].add(['.hgignore', 'series'])
2175 commands.add(ui, r)
2175 commands.add(ui, r)
2176 return 0
2176 return 0
2177
2177
2178 @command("^qinit",
2178 @command("^qinit",
2179 [('c', 'create-repo', None, _('create queue repository'))],
2179 [('c', 'create-repo', None, _('create queue repository'))],
2180 _('hg qinit [-c]'))
2180 _('hg qinit [-c]'))
2181 def init(ui, repo, **opts):
2181 def init(ui, repo, **opts):
2182 """init a new queue repository (DEPRECATED)
2182 """init a new queue repository (DEPRECATED)
2183
2183
2184 The queue repository is unversioned by default. If
2184 The queue repository is unversioned by default. If
2185 -c/--create-repo is specified, qinit will create a separate nested
2185 -c/--create-repo is specified, qinit will create a separate nested
2186 repository for patches (qinit -c may also be run later to convert
2186 repository for patches (qinit -c may also be run later to convert
2187 an unversioned patch repository into a versioned one). You can use
2187 an unversioned patch repository into a versioned one). You can use
2188 qcommit to commit changes to this queue repository.
2188 qcommit to commit changes to this queue repository.
2189
2189
2190 This command is deprecated. Without -c, it's implied by other relevant
2190 This command is deprecated. Without -c, it's implied by other relevant
2191 commands. With -c, use :hg:`init --mq` instead."""
2191 commands. With -c, use :hg:`init --mq` instead."""
2192 return qinit(ui, repo, create=opts.get('create_repo'))
2192 return qinit(ui, repo, create=opts.get('create_repo'))
2193
2193
2194 @command("qclone",
2194 @command("qclone",
2195 [('', 'pull', None, _('use pull protocol to copy metadata')),
2195 [('', 'pull', None, _('use pull protocol to copy metadata')),
2196 ('U', 'noupdate', None,
2196 ('U', 'noupdate', None,
2197 _('do not update the new working directories')),
2197 _('do not update the new working directories')),
2198 ('', 'uncompressed', None,
2198 ('', 'uncompressed', None,
2199 _('use uncompressed transfer (fast over LAN)')),
2199 _('use uncompressed transfer (fast over LAN)')),
2200 ('p', 'patches', '',
2200 ('p', 'patches', '',
2201 _('location of source patch repository'), _('REPO')),
2201 _('location of source patch repository'), _('REPO')),
2202 ] + commands.remoteopts,
2202 ] + commands.remoteopts,
2203 _('hg qclone [OPTION]... SOURCE [DEST]'))
2203 _('hg qclone [OPTION]... SOURCE [DEST]'))
2204 def clone(ui, source, dest=None, **opts):
2204 def clone(ui, source, dest=None, **opts):
2205 '''clone main and patch repository at same time
2205 '''clone main and patch repository at same time
2206
2206
2207 If source is local, destination will have no patches applied. If
2207 If source is local, destination will have no patches applied. If
2208 source is remote, this command can not check if patches are
2208 source is remote, this command can not check if patches are
2209 applied in source, so cannot guarantee that patches are not
2209 applied in source, so cannot guarantee that patches are not
2210 applied in destination. If you clone remote repository, be sure
2210 applied in destination. If you clone remote repository, be sure
2211 before that it has no patches applied.
2211 before that it has no patches applied.
2212
2212
2213 Source patch repository is looked for in <src>/.hg/patches by
2213 Source patch repository is looked for in <src>/.hg/patches by
2214 default. Use -p <url> to change.
2214 default. Use -p <url> to change.
2215
2215
2216 The patch directory must be a nested Mercurial repository, as
2216 The patch directory must be a nested Mercurial repository, as
2217 would be created by :hg:`init --mq`.
2217 would be created by :hg:`init --mq`.
2218
2218
2219 Return 0 on success.
2219 Return 0 on success.
2220 '''
2220 '''
2221 def patchdir(repo):
2221 def patchdir(repo):
2222 """compute a patch repo url from a repo object"""
2222 """compute a patch repo url from a repo object"""
2223 url = repo.url()
2223 url = repo.url()
2224 if url.endswith('/'):
2224 if url.endswith('/'):
2225 url = url[:-1]
2225 url = url[:-1]
2226 return url + '/.hg/patches'
2226 return url + '/.hg/patches'
2227
2227
2228 # main repo (destination and sources)
2228 # main repo (destination and sources)
2229 if dest is None:
2229 if dest is None:
2230 dest = hg.defaultdest(source)
2230 dest = hg.defaultdest(source)
2231 sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
2231 sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
2232
2232
2233 # patches repo (source only)
2233 # patches repo (source only)
2234 if opts.get('patches'):
2234 if opts.get('patches'):
2235 patchespath = ui.expandpath(opts.get('patches'))
2235 patchespath = ui.expandpath(opts.get('patches'))
2236 else:
2236 else:
2237 patchespath = patchdir(sr)
2237 patchespath = patchdir(sr)
2238 try:
2238 try:
2239 hg.repository(ui, patchespath)
2239 hg.repository(ui, patchespath)
2240 except error.RepoError:
2240 except error.RepoError:
2241 raise util.Abort(_('versioned patch repository not found'
2241 raise util.Abort(_('versioned patch repository not found'
2242 ' (see init --mq)'))
2242 ' (see init --mq)'))
2243 qbase, destrev = None, None
2243 qbase, destrev = None, None
2244 if sr.local():
2244 if sr.local():
2245 if sr.mq.applied and sr[qbase].phase() != phases.secret:
2245 if sr.mq.applied and sr[qbase].phase() != phases.secret:
2246 qbase = sr.mq.applied[0].node
2246 qbase = sr.mq.applied[0].node
2247 if not hg.islocal(dest):
2247 if not hg.islocal(dest):
2248 heads = set(sr.heads())
2248 heads = set(sr.heads())
2249 destrev = list(heads.difference(sr.heads(qbase)))
2249 destrev = list(heads.difference(sr.heads(qbase)))
2250 destrev.append(sr.changelog.parents(qbase)[0])
2250 destrev.append(sr.changelog.parents(qbase)[0])
2251 elif sr.capable('lookup'):
2251 elif sr.capable('lookup'):
2252 try:
2252 try:
2253 qbase = sr.lookup('qbase')
2253 qbase = sr.lookup('qbase')
2254 except error.RepoError:
2254 except error.RepoError:
2255 pass
2255 pass
2256
2256
2257 ui.note(_('cloning main repository\n'))
2257 ui.note(_('cloning main repository\n'))
2258 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2258 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2259 pull=opts.get('pull'),
2259 pull=opts.get('pull'),
2260 rev=destrev,
2260 rev=destrev,
2261 update=False,
2261 update=False,
2262 stream=opts.get('uncompressed'))
2262 stream=opts.get('uncompressed'))
2263
2263
2264 ui.note(_('cloning patch repository\n'))
2264 ui.note(_('cloning patch repository\n'))
2265 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2265 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2266 pull=opts.get('pull'), update=not opts.get('noupdate'),
2266 pull=opts.get('pull'), update=not opts.get('noupdate'),
2267 stream=opts.get('uncompressed'))
2267 stream=opts.get('uncompressed'))
2268
2268
2269 if dr.local():
2269 if dr.local():
2270 if qbase:
2270 if qbase:
2271 ui.note(_('stripping applied patches from destination '
2271 ui.note(_('stripping applied patches from destination '
2272 'repository\n'))
2272 'repository\n'))
2273 dr.mq.strip(dr, [qbase], update=False, backup=None)
2273 dr.mq.strip(dr, [qbase], update=False, backup=None)
2274 if not opts.get('noupdate'):
2274 if not opts.get('noupdate'):
2275 ui.note(_('updating destination repository\n'))
2275 ui.note(_('updating destination repository\n'))
2276 hg.update(dr, dr.changelog.tip())
2276 hg.update(dr, dr.changelog.tip())
2277
2277
2278 @command("qcommit|qci",
2278 @command("qcommit|qci",
2279 commands.table["^commit|ci"][1],
2279 commands.table["^commit|ci"][1],
2280 _('hg qcommit [OPTION]... [FILE]...'))
2280 _('hg qcommit [OPTION]... [FILE]...'))
2281 def commit(ui, repo, *pats, **opts):
2281 def commit(ui, repo, *pats, **opts):
2282 """commit changes in the queue repository (DEPRECATED)
2282 """commit changes in the queue repository (DEPRECATED)
2283
2283
2284 This command is deprecated; use :hg:`commit --mq` instead."""
2284 This command is deprecated; use :hg:`commit --mq` instead."""
2285 q = repo.mq
2285 q = repo.mq
2286 r = q.qrepo()
2286 r = q.qrepo()
2287 if not r:
2287 if not r:
2288 raise util.Abort('no queue repository')
2288 raise util.Abort('no queue repository')
2289 commands.commit(r.ui, r, *pats, **opts)
2289 commands.commit(r.ui, r, *pats, **opts)
2290
2290
2291 @command("qseries",
2291 @command("qseries",
2292 [('m', 'missing', None, _('print patches not in series')),
2292 [('m', 'missing', None, _('print patches not in series')),
2293 ] + seriesopts,
2293 ] + seriesopts,
2294 _('hg qseries [-ms]'))
2294 _('hg qseries [-ms]'))
2295 def series(ui, repo, **opts):
2295 def series(ui, repo, **opts):
2296 """print the entire series file
2296 """print the entire series file
2297
2297
2298 Returns 0 on success."""
2298 Returns 0 on success."""
2299 repo.mq.qseries(repo, missing=opts.get('missing'),
2299 repo.mq.qseries(repo, missing=opts.get('missing'),
2300 summary=opts.get('summary'))
2300 summary=opts.get('summary'))
2301 return 0
2301 return 0
2302
2302
2303 @command("qtop", seriesopts, _('hg qtop [-s]'))
2303 @command("qtop", seriesopts, _('hg qtop [-s]'))
2304 def top(ui, repo, **opts):
2304 def top(ui, repo, **opts):
2305 """print the name of the current patch
2305 """print the name of the current patch
2306
2306
2307 Returns 0 on success."""
2307 Returns 0 on success."""
2308 q = repo.mq
2308 q = repo.mq
2309 t = q.applied and q.seriesend(True) or 0
2309 t = q.applied and q.seriesend(True) or 0
2310 if t:
2310 if t:
2311 q.qseries(repo, start=t - 1, length=1, status='A',
2311 q.qseries(repo, start=t - 1, length=1, status='A',
2312 summary=opts.get('summary'))
2312 summary=opts.get('summary'))
2313 else:
2313 else:
2314 ui.write(_("no patches applied\n"))
2314 ui.write(_("no patches applied\n"))
2315 return 1
2315 return 1
2316
2316
2317 @command("qnext", seriesopts, _('hg qnext [-s]'))
2317 @command("qnext", seriesopts, _('hg qnext [-s]'))
2318 def next(ui, repo, **opts):
2318 def next(ui, repo, **opts):
2319 """print the name of the next pushable patch
2319 """print the name of the next pushable patch
2320
2320
2321 Returns 0 on success."""
2321 Returns 0 on success."""
2322 q = repo.mq
2322 q = repo.mq
2323 end = q.seriesend()
2323 end = q.seriesend()
2324 if end == len(q.series):
2324 if end == len(q.series):
2325 ui.write(_("all patches applied\n"))
2325 ui.write(_("all patches applied\n"))
2326 return 1
2326 return 1
2327 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2327 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2328
2328
2329 @command("qprev", seriesopts, _('hg qprev [-s]'))
2329 @command("qprev", seriesopts, _('hg qprev [-s]'))
2330 def prev(ui, repo, **opts):
2330 def prev(ui, repo, **opts):
2331 """print the name of the preceding applied patch
2331 """print the name of the preceding applied patch
2332
2332
2333 Returns 0 on success."""
2333 Returns 0 on success."""
2334 q = repo.mq
2334 q = repo.mq
2335 l = len(q.applied)
2335 l = len(q.applied)
2336 if l == 1:
2336 if l == 1:
2337 ui.write(_("only one patch applied\n"))
2337 ui.write(_("only one patch applied\n"))
2338 return 1
2338 return 1
2339 if not l:
2339 if not l:
2340 ui.write(_("no patches applied\n"))
2340 ui.write(_("no patches applied\n"))
2341 return 1
2341 return 1
2342 idx = q.series.index(q.applied[-2].name)
2342 idx = q.series.index(q.applied[-2].name)
2343 q.qseries(repo, start=idx, length=1, status='A',
2343 q.qseries(repo, start=idx, length=1, status='A',
2344 summary=opts.get('summary'))
2344 summary=opts.get('summary'))
2345
2345
2346 def setupheaderopts(ui, opts):
2346 def setupheaderopts(ui, opts):
2347 if not opts.get('user') and opts.get('currentuser'):
2347 if not opts.get('user') and opts.get('currentuser'):
2348 opts['user'] = ui.username()
2348 opts['user'] = ui.username()
2349 if not opts.get('date') and opts.get('currentdate'):
2349 if not opts.get('date') and opts.get('currentdate'):
2350 opts['date'] = "%d %d" % util.makedate()
2350 opts['date'] = "%d %d" % util.makedate()
2351
2351
2352 @command("^qnew",
2352 @command("^qnew",
2353 [('e', 'edit', None, _('edit commit message')),
2353 [('e', 'edit', None, _('edit commit message')),
2354 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2354 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2355 ('g', 'git', None, _('use git extended diff format')),
2355 ('g', 'git', None, _('use git extended diff format')),
2356 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2356 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2357 ('u', 'user', '',
2357 ('u', 'user', '',
2358 _('add "From: <USER>" to patch'), _('USER')),
2358 _('add "From: <USER>" to patch'), _('USER')),
2359 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2359 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2360 ('d', 'date', '',
2360 ('d', 'date', '',
2361 _('add "Date: <DATE>" to patch'), _('DATE'))
2361 _('add "Date: <DATE>" to patch'), _('DATE'))
2362 ] + commands.walkopts + commands.commitopts,
2362 ] + commands.walkopts + commands.commitopts,
2363 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'))
2363 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'))
2364 def new(ui, repo, patch, *args, **opts):
2364 def new(ui, repo, patch, *args, **opts):
2365 """create a new patch
2365 """create a new patch
2366
2366
2367 qnew creates a new patch on top of the currently-applied patch (if
2367 qnew creates a new patch on top of the currently-applied patch (if
2368 any). The patch will be initialized with any outstanding changes
2368 any). The patch will be initialized with any outstanding changes
2369 in the working directory. You may also use -I/--include,
2369 in the working directory. You may also use -I/--include,
2370 -X/--exclude, and/or a list of files after the patch name to add
2370 -X/--exclude, and/or a list of files after the patch name to add
2371 only changes to matching files to the new patch, leaving the rest
2371 only changes to matching files to the new patch, leaving the rest
2372 as uncommitted modifications.
2372 as uncommitted modifications.
2373
2373
2374 -u/--user and -d/--date can be used to set the (given) user and
2374 -u/--user and -d/--date can be used to set the (given) user and
2375 date, respectively. -U/--currentuser and -D/--currentdate set user
2375 date, respectively. -U/--currentuser and -D/--currentdate set user
2376 to current user and date to current date.
2376 to current user and date to current date.
2377
2377
2378 -e/--edit, -m/--message or -l/--logfile set the patch header as
2378 -e/--edit, -m/--message or -l/--logfile set the patch header as
2379 well as the commit message. If none is specified, the header is
2379 well as the commit message. If none is specified, the header is
2380 empty and the commit message is '[mq]: PATCH'.
2380 empty and the commit message is '[mq]: PATCH'.
2381
2381
2382 Use the -g/--git option to keep the patch in the git extended diff
2382 Use the -g/--git option to keep the patch in the git extended diff
2383 format. Read the diffs help topic for more information on why this
2383 format. Read the diffs help topic for more information on why this
2384 is important for preserving permission changes and copy/rename
2384 is important for preserving permission changes and copy/rename
2385 information.
2385 information.
2386
2386
2387 Returns 0 on successful creation of a new patch.
2387 Returns 0 on successful creation of a new patch.
2388 """
2388 """
2389 msg = cmdutil.logmessage(ui, opts)
2389 msg = cmdutil.logmessage(ui, opts)
2390 def getmsg():
2390 def getmsg():
2391 return ui.edit(msg, opts.get('user') or ui.username())
2391 return ui.edit(msg, opts.get('user') or ui.username())
2392 q = repo.mq
2392 q = repo.mq
2393 opts['msg'] = msg
2393 opts['msg'] = msg
2394 if opts.get('edit'):
2394 if opts.get('edit'):
2395 opts['msg'] = getmsg
2395 opts['msg'] = getmsg
2396 else:
2396 else:
2397 opts['msg'] = msg
2397 opts['msg'] = msg
2398 setupheaderopts(ui, opts)
2398 setupheaderopts(ui, opts)
2399 q.new(repo, patch, *args, **opts)
2399 q.new(repo, patch, *args, **opts)
2400 q.savedirty()
2400 q.savedirty()
2401 return 0
2401 return 0
2402
2402
2403 @command("^qrefresh",
2403 @command("^qrefresh",
2404 [('e', 'edit', None, _('edit commit message')),
2404 [('e', 'edit', None, _('edit commit message')),
2405 ('g', 'git', None, _('use git extended diff format')),
2405 ('g', 'git', None, _('use git extended diff format')),
2406 ('s', 'short', None,
2406 ('s', 'short', None,
2407 _('refresh only files already in the patch and specified files')),
2407 _('refresh only files already in the patch and specified files')),
2408 ('U', 'currentuser', None,
2408 ('U', 'currentuser', None,
2409 _('add/update author field in patch with current user')),
2409 _('add/update author field in patch with current user')),
2410 ('u', 'user', '',
2410 ('u', 'user', '',
2411 _('add/update author field in patch with given user'), _('USER')),
2411 _('add/update author field in patch with given user'), _('USER')),
2412 ('D', 'currentdate', None,
2412 ('D', 'currentdate', None,
2413 _('add/update date field in patch with current date')),
2413 _('add/update date field in patch with current date')),
2414 ('d', 'date', '',
2414 ('d', 'date', '',
2415 _('add/update date field in patch with given date'), _('DATE'))
2415 _('add/update date field in patch with given date'), _('DATE'))
2416 ] + commands.walkopts + commands.commitopts,
2416 ] + commands.walkopts + commands.commitopts,
2417 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'))
2417 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'))
2418 def refresh(ui, repo, *pats, **opts):
2418 def refresh(ui, repo, *pats, **opts):
2419 """update the current patch
2419 """update the current patch
2420
2420
2421 If any file patterns are provided, the refreshed patch will
2421 If any file patterns are provided, the refreshed patch will
2422 contain only the modifications that match those patterns; the
2422 contain only the modifications that match those patterns; the
2423 remaining modifications will remain in the working directory.
2423 remaining modifications will remain in the working directory.
2424
2424
2425 If -s/--short is specified, files currently included in the patch
2425 If -s/--short is specified, files currently included in the patch
2426 will be refreshed just like matched files and remain in the patch.
2426 will be refreshed just like matched files and remain in the patch.
2427
2427
2428 If -e/--edit is specified, Mercurial will start your configured editor for
2428 If -e/--edit is specified, Mercurial will start your configured editor for
2429 you to enter a message. In case qrefresh fails, you will find a backup of
2429 you to enter a message. In case qrefresh fails, you will find a backup of
2430 your message in ``.hg/last-message.txt``.
2430 your message in ``.hg/last-message.txt``.
2431
2431
2432 hg add/remove/copy/rename work as usual, though you might want to
2432 hg add/remove/copy/rename work as usual, though you might want to
2433 use git-style patches (-g/--git or [diff] git=1) to track copies
2433 use git-style patches (-g/--git or [diff] git=1) to track copies
2434 and renames. See the diffs help topic for more information on the
2434 and renames. See the diffs help topic for more information on the
2435 git diff format.
2435 git diff format.
2436
2436
2437 Returns 0 on success.
2437 Returns 0 on success.
2438 """
2438 """
2439 q = repo.mq
2439 q = repo.mq
2440 message = cmdutil.logmessage(ui, opts)
2440 message = cmdutil.logmessage(ui, opts)
2441 if opts.get('edit'):
2441 if opts.get('edit'):
2442 if not q.applied:
2442 if not q.applied:
2443 ui.write(_("no patches applied\n"))
2443 ui.write(_("no patches applied\n"))
2444 return 1
2444 return 1
2445 if message:
2445 if message:
2446 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2446 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2447 patch = q.applied[-1].name
2447 patch = q.applied[-1].name
2448 ph = patchheader(q.join(patch), q.plainmode)
2448 ph = patchheader(q.join(patch), q.plainmode)
2449 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2449 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2450 # We don't want to lose the patch message if qrefresh fails (issue2062)
2450 # We don't want to lose the patch message if qrefresh fails (issue2062)
2451 repo.savecommitmessage(message)
2451 repo.savecommitmessage(message)
2452 setupheaderopts(ui, opts)
2452 setupheaderopts(ui, opts)
2453 wlock = repo.wlock()
2453 wlock = repo.wlock()
2454 try:
2454 try:
2455 ret = q.refresh(repo, pats, msg=message, **opts)
2455 ret = q.refresh(repo, pats, msg=message, **opts)
2456 q.savedirty()
2456 q.savedirty()
2457 return ret
2457 return ret
2458 finally:
2458 finally:
2459 wlock.release()
2459 wlock.release()
2460
2460
2461 @command("^qdiff",
2461 @command("^qdiff",
2462 commands.diffopts + commands.diffopts2 + commands.walkopts,
2462 commands.diffopts + commands.diffopts2 + commands.walkopts,
2463 _('hg qdiff [OPTION]... [FILE]...'))
2463 _('hg qdiff [OPTION]... [FILE]...'))
2464 def diff(ui, repo, *pats, **opts):
2464 def diff(ui, repo, *pats, **opts):
2465 """diff of the current patch and subsequent modifications
2465 """diff of the current patch and subsequent modifications
2466
2466
2467 Shows a diff which includes the current patch as well as any
2467 Shows a diff which includes the current patch as well as any
2468 changes which have been made in the working directory since the
2468 changes which have been made in the working directory since the
2469 last refresh (thus showing what the current patch would become
2469 last refresh (thus showing what the current patch would become
2470 after a qrefresh).
2470 after a qrefresh).
2471
2471
2472 Use :hg:`diff` if you only want to see the changes made since the
2472 Use :hg:`diff` if you only want to see the changes made since the
2473 last qrefresh, or :hg:`export qtip` if you want to see changes
2473 last qrefresh, or :hg:`export qtip` if you want to see changes
2474 made by the current patch without including changes made since the
2474 made by the current patch without including changes made since the
2475 qrefresh.
2475 qrefresh.
2476
2476
2477 Returns 0 on success.
2477 Returns 0 on success.
2478 """
2478 """
2479 repo.mq.diff(repo, pats, opts)
2479 repo.mq.diff(repo, pats, opts)
2480 return 0
2480 return 0
2481
2481
2482 @command('qfold',
2482 @command('qfold',
2483 [('e', 'edit', None, _('edit patch header')),
2483 [('e', 'edit', None, _('edit patch header')),
2484 ('k', 'keep', None, _('keep folded patch files')),
2484 ('k', 'keep', None, _('keep folded patch files')),
2485 ] + commands.commitopts,
2485 ] + commands.commitopts,
2486 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2486 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2487 def fold(ui, repo, *files, **opts):
2487 def fold(ui, repo, *files, **opts):
2488 """fold the named patches into the current patch
2488 """fold the named patches into the current patch
2489
2489
2490 Patches must not yet be applied. Each patch will be successively
2490 Patches must not yet be applied. Each patch will be successively
2491 applied to the current patch in the order given. If all the
2491 applied to the current patch in the order given. If all the
2492 patches apply successfully, the current patch will be refreshed
2492 patches apply successfully, the current patch will be refreshed
2493 with the new cumulative patch, and the folded patches will be
2493 with the new cumulative patch, and the folded patches will be
2494 deleted. With -k/--keep, the folded patch files will not be
2494 deleted. With -k/--keep, the folded patch files will not be
2495 removed afterwards.
2495 removed afterwards.
2496
2496
2497 The header for each folded patch will be concatenated with the
2497 The header for each folded patch will be concatenated with the
2498 current patch header, separated by a line of ``* * *``.
2498 current patch header, separated by a line of ``* * *``.
2499
2499
2500 Returns 0 on success."""
2500 Returns 0 on success."""
2501 q = repo.mq
2501 q = repo.mq
2502 if not files:
2502 if not files:
2503 raise util.Abort(_('qfold requires at least one patch name'))
2503 raise util.Abort(_('qfold requires at least one patch name'))
2504 if not q.checktoppatch(repo)[0]:
2504 if not q.checktoppatch(repo)[0]:
2505 raise util.Abort(_('no patches applied'))
2505 raise util.Abort(_('no patches applied'))
2506 q.checklocalchanges(repo)
2506 q.checklocalchanges(repo)
2507
2507
2508 message = cmdutil.logmessage(ui, opts)
2508 message = cmdutil.logmessage(ui, opts)
2509 if opts.get('edit'):
2509 if opts.get('edit'):
2510 if message:
2510 if message:
2511 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2511 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2512
2512
2513 parent = q.lookup('qtip')
2513 parent = q.lookup('qtip')
2514 patches = []
2514 patches = []
2515 messages = []
2515 messages = []
2516 for f in files:
2516 for f in files:
2517 p = q.lookup(f)
2517 p = q.lookup(f)
2518 if p in patches or p == parent:
2518 if p in patches or p == parent:
2519 ui.warn(_('skipping already folded patch %s\n') % p)
2519 ui.warn(_('skipping already folded patch %s\n') % p)
2520 if q.isapplied(p):
2520 if q.isapplied(p):
2521 raise util.Abort(_('qfold cannot fold already applied patch %s')
2521 raise util.Abort(_('qfold cannot fold already applied patch %s')
2522 % p)
2522 % p)
2523 patches.append(p)
2523 patches.append(p)
2524
2524
2525 for p in patches:
2525 for p in patches:
2526 if not message:
2526 if not message:
2527 ph = patchheader(q.join(p), q.plainmode)
2527 ph = patchheader(q.join(p), q.plainmode)
2528 if ph.message:
2528 if ph.message:
2529 messages.append(ph.message)
2529 messages.append(ph.message)
2530 pf = q.join(p)
2530 pf = q.join(p)
2531 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2531 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2532 if not patchsuccess:
2532 if not patchsuccess:
2533 raise util.Abort(_('error folding patch %s') % p)
2533 raise util.Abort(_('error folding patch %s') % p)
2534
2534
2535 if not message:
2535 if not message:
2536 ph = patchheader(q.join(parent), q.plainmode)
2536 ph = patchheader(q.join(parent), q.plainmode)
2537 message, user = ph.message, ph.user
2537 message, user = ph.message, ph.user
2538 for msg in messages:
2538 for msg in messages:
2539 message.append('* * *')
2539 message.append('* * *')
2540 message.extend(msg)
2540 message.extend(msg)
2541 message = '\n'.join(message)
2541 message = '\n'.join(message)
2542
2542
2543 if opts.get('edit'):
2543 if opts.get('edit'):
2544 message = ui.edit(message, user or ui.username())
2544 message = ui.edit(message, user or ui.username())
2545
2545
2546 diffopts = q.patchopts(q.diffopts(), *patches)
2546 diffopts = q.patchopts(q.diffopts(), *patches)
2547 wlock = repo.wlock()
2547 wlock = repo.wlock()
2548 try:
2548 try:
2549 q.refresh(repo, msg=message, git=diffopts.git)
2549 q.refresh(repo, msg=message, git=diffopts.git)
2550 q.delete(repo, patches, opts)
2550 q.delete(repo, patches, opts)
2551 q.savedirty()
2551 q.savedirty()
2552 finally:
2552 finally:
2553 wlock.release()
2553 wlock.release()
2554
2554
2555 @command("qgoto",
2555 @command("qgoto",
2556 [('', 'keep-changes', None,
2556 [('', 'keep-changes', None,
2557 _('tolerate non-conflicting local changes')),
2557 _('tolerate non-conflicting local changes')),
2558 ('f', 'force', None, _('overwrite any local changes')),
2558 ('f', 'force', None, _('overwrite any local changes')),
2559 ('', 'no-backup', None, _('do not save backup copies of files'))],
2559 ('', 'no-backup', None, _('do not save backup copies of files'))],
2560 _('hg qgoto [OPTION]... PATCH'))
2560 _('hg qgoto [OPTION]... PATCH'))
2561 def goto(ui, repo, patch, **opts):
2561 def goto(ui, repo, patch, **opts):
2562 '''push or pop patches until named patch is at top of stack
2562 '''push or pop patches until named patch is at top of stack
2563
2563
2564 Returns 0 on success.'''
2564 Returns 0 on success.'''
2565 opts = fixkeepchangesopts(ui, opts)
2565 opts = fixkeepchangesopts(ui, opts)
2566 q = repo.mq
2566 q = repo.mq
2567 patch = q.lookup(patch)
2567 patch = q.lookup(patch)
2568 nobackup = opts.get('no_backup')
2568 nobackup = opts.get('no_backup')
2569 keepchanges = opts.get('keep_changes')
2569 keepchanges = opts.get('keep_changes')
2570 if q.isapplied(patch):
2570 if q.isapplied(patch):
2571 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2571 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2572 keepchanges=keepchanges)
2572 keepchanges=keepchanges)
2573 else:
2573 else:
2574 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2574 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2575 keepchanges=keepchanges)
2575 keepchanges=keepchanges)
2576 q.savedirty()
2576 q.savedirty()
2577 return ret
2577 return ret
2578
2578
2579 @command("qguard",
2579 @command("qguard",
2580 [('l', 'list', None, _('list all patches and guards')),
2580 [('l', 'list', None, _('list all patches and guards')),
2581 ('n', 'none', None, _('drop all guards'))],
2581 ('n', 'none', None, _('drop all guards'))],
2582 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2582 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2583 def guard(ui, repo, *args, **opts):
2583 def guard(ui, repo, *args, **opts):
2584 '''set or print guards for a patch
2584 '''set or print guards for a patch
2585
2585
2586 Guards control whether a patch can be pushed. A patch with no
2586 Guards control whether a patch can be pushed. A patch with no
2587 guards is always pushed. A patch with a positive guard ("+foo") is
2587 guards is always pushed. A patch with a positive guard ("+foo") is
2588 pushed only if the :hg:`qselect` command has activated it. A patch with
2588 pushed only if the :hg:`qselect` command has activated it. A patch with
2589 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2589 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2590 has activated it.
2590 has activated it.
2591
2591
2592 With no arguments, print the currently active guards.
2592 With no arguments, print the currently active guards.
2593 With arguments, set guards for the named patch.
2593 With arguments, set guards for the named patch.
2594
2594
2595 .. note::
2595 .. note::
2596 Specifying negative guards now requires '--'.
2596 Specifying negative guards now requires '--'.
2597
2597
2598 To set guards on another patch::
2598 To set guards on another patch::
2599
2599
2600 hg qguard other.patch -- +2.6.17 -stable
2600 hg qguard other.patch -- +2.6.17 -stable
2601
2601
2602 Returns 0 on success.
2602 Returns 0 on success.
2603 '''
2603 '''
2604 def status(idx):
2604 def status(idx):
2605 guards = q.seriesguards[idx] or ['unguarded']
2605 guards = q.seriesguards[idx] or ['unguarded']
2606 if q.series[idx] in applied:
2606 if q.series[idx] in applied:
2607 state = 'applied'
2607 state = 'applied'
2608 elif q.pushable(idx)[0]:
2608 elif q.pushable(idx)[0]:
2609 state = 'unapplied'
2609 state = 'unapplied'
2610 else:
2610 else:
2611 state = 'guarded'
2611 state = 'guarded'
2612 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2612 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2613 ui.write('%s: ' % ui.label(q.series[idx], label))
2613 ui.write('%s: ' % ui.label(q.series[idx], label))
2614
2614
2615 for i, guard in enumerate(guards):
2615 for i, guard in enumerate(guards):
2616 if guard.startswith('+'):
2616 if guard.startswith('+'):
2617 ui.write(guard, label='qguard.positive')
2617 ui.write(guard, label='qguard.positive')
2618 elif guard.startswith('-'):
2618 elif guard.startswith('-'):
2619 ui.write(guard, label='qguard.negative')
2619 ui.write(guard, label='qguard.negative')
2620 else:
2620 else:
2621 ui.write(guard, label='qguard.unguarded')
2621 ui.write(guard, label='qguard.unguarded')
2622 if i != len(guards) - 1:
2622 if i != len(guards) - 1:
2623 ui.write(' ')
2623 ui.write(' ')
2624 ui.write('\n')
2624 ui.write('\n')
2625 q = repo.mq
2625 q = repo.mq
2626 applied = set(p.name for p in q.applied)
2626 applied = set(p.name for p in q.applied)
2627 patch = None
2627 patch = None
2628 args = list(args)
2628 args = list(args)
2629 if opts.get('list'):
2629 if opts.get('list'):
2630 if args or opts.get('none'):
2630 if args or opts.get('none'):
2631 raise util.Abort(_('cannot mix -l/--list with options or '
2631 raise util.Abort(_('cannot mix -l/--list with options or '
2632 'arguments'))
2632 'arguments'))
2633 for i in xrange(len(q.series)):
2633 for i in xrange(len(q.series)):
2634 status(i)
2634 status(i)
2635 return
2635 return
2636 if not args or args[0][0:1] in '-+':
2636 if not args or args[0][0:1] in '-+':
2637 if not q.applied:
2637 if not q.applied:
2638 raise util.Abort(_('no patches applied'))
2638 raise util.Abort(_('no patches applied'))
2639 patch = q.applied[-1].name
2639 patch = q.applied[-1].name
2640 if patch is None and args[0][0:1] not in '-+':
2640 if patch is None and args[0][0:1] not in '-+':
2641 patch = args.pop(0)
2641 patch = args.pop(0)
2642 if patch is None:
2642 if patch is None:
2643 raise util.Abort(_('no patch to work with'))
2643 raise util.Abort(_('no patch to work with'))
2644 if args or opts.get('none'):
2644 if args or opts.get('none'):
2645 idx = q.findseries(patch)
2645 idx = q.findseries(patch)
2646 if idx is None:
2646 if idx is None:
2647 raise util.Abort(_('no patch named %s') % patch)
2647 raise util.Abort(_('no patch named %s') % patch)
2648 q.setguards(idx, args)
2648 q.setguards(idx, args)
2649 q.savedirty()
2649 q.savedirty()
2650 else:
2650 else:
2651 status(q.series.index(q.lookup(patch)))
2651 status(q.series.index(q.lookup(patch)))
2652
2652
2653 @command("qheader", [], _('hg qheader [PATCH]'))
2653 @command("qheader", [], _('hg qheader [PATCH]'))
2654 def header(ui, repo, patch=None):
2654 def header(ui, repo, patch=None):
2655 """print the header of the topmost or specified patch
2655 """print the header of the topmost or specified patch
2656
2656
2657 Returns 0 on success."""
2657 Returns 0 on success."""
2658 q = repo.mq
2658 q = repo.mq
2659
2659
2660 if patch:
2660 if patch:
2661 patch = q.lookup(patch)
2661 patch = q.lookup(patch)
2662 else:
2662 else:
2663 if not q.applied:
2663 if not q.applied:
2664 ui.write(_('no patches applied\n'))
2664 ui.write(_('no patches applied\n'))
2665 return 1
2665 return 1
2666 patch = q.lookup('qtip')
2666 patch = q.lookup('qtip')
2667 ph = patchheader(q.join(patch), q.plainmode)
2667 ph = patchheader(q.join(patch), q.plainmode)
2668
2668
2669 ui.write('\n'.join(ph.message) + '\n')
2669 ui.write('\n'.join(ph.message) + '\n')
2670
2670
2671 def lastsavename(path):
2671 def lastsavename(path):
2672 (directory, base) = os.path.split(path)
2672 (directory, base) = os.path.split(path)
2673 names = os.listdir(directory)
2673 names = os.listdir(directory)
2674 namere = re.compile("%s.([0-9]+)" % base)
2674 namere = re.compile("%s.([0-9]+)" % base)
2675 maxindex = None
2675 maxindex = None
2676 maxname = None
2676 maxname = None
2677 for f in names:
2677 for f in names:
2678 m = namere.match(f)
2678 m = namere.match(f)
2679 if m:
2679 if m:
2680 index = int(m.group(1))
2680 index = int(m.group(1))
2681 if maxindex is None or index > maxindex:
2681 if maxindex is None or index > maxindex:
2682 maxindex = index
2682 maxindex = index
2683 maxname = f
2683 maxname = f
2684 if maxname:
2684 if maxname:
2685 return (os.path.join(directory, maxname), maxindex)
2685 return (os.path.join(directory, maxname), maxindex)
2686 return (None, None)
2686 return (None, None)
2687
2687
2688 def savename(path):
2688 def savename(path):
2689 (last, index) = lastsavename(path)
2689 (last, index) = lastsavename(path)
2690 if last is None:
2690 if last is None:
2691 index = 0
2691 index = 0
2692 newpath = path + ".%d" % (index + 1)
2692 newpath = path + ".%d" % (index + 1)
2693 return newpath
2693 return newpath
2694
2694
2695 @command("^qpush",
2695 @command("^qpush",
2696 [('', 'keep-changes', None,
2696 [('', 'keep-changes', None,
2697 _('tolerate non-conflicting local changes')),
2697 _('tolerate non-conflicting local changes')),
2698 ('f', 'force', None, _('apply on top of local changes')),
2698 ('f', 'force', None, _('apply on top of local changes')),
2699 ('e', 'exact', None,
2699 ('e', 'exact', None,
2700 _('apply the target patch to its recorded parent')),
2700 _('apply the target patch to its recorded parent')),
2701 ('l', 'list', None, _('list patch name in commit text')),
2701 ('l', 'list', None, _('list patch name in commit text')),
2702 ('a', 'all', None, _('apply all patches')),
2702 ('a', 'all', None, _('apply all patches')),
2703 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2703 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2704 ('n', 'name', '',
2704 ('n', 'name', '',
2705 _('merge queue name (DEPRECATED)'), _('NAME')),
2705 _('merge queue name (DEPRECATED)'), _('NAME')),
2706 ('', 'move', None,
2706 ('', 'move', None,
2707 _('reorder patch series and apply only the patch')),
2707 _('reorder patch series and apply only the patch')),
2708 ('', 'no-backup', None, _('do not save backup copies of files'))],
2708 ('', 'no-backup', None, _('do not save backup copies of files'))],
2709 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2709 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2710 def push(ui, repo, patch=None, **opts):
2710 def push(ui, repo, patch=None, **opts):
2711 """push the next patch onto the stack
2711 """push the next patch onto the stack
2712
2712
2713 By default, abort if the working directory contains uncommitted
2713 By default, abort if the working directory contains uncommitted
2714 changes. With --keep-changes, abort only if the uncommitted files
2714 changes. With --keep-changes, abort only if the uncommitted files
2715 overlap with patched files. With -f/--force, backup and patch over
2715 overlap with patched files. With -f/--force, backup and patch over
2716 uncommitted changes.
2716 uncommitted changes.
2717
2717
2718 Return 0 on success.
2718 Return 0 on success.
2719 """
2719 """
2720 q = repo.mq
2720 q = repo.mq
2721 mergeq = None
2721 mergeq = None
2722
2722
2723 opts = fixkeepchangesopts(ui, opts)
2723 opts = fixkeepchangesopts(ui, opts)
2724 if opts.get('merge'):
2724 if opts.get('merge'):
2725 if opts.get('name'):
2725 if opts.get('name'):
2726 newpath = repo.join(opts.get('name'))
2726 newpath = repo.join(opts.get('name'))
2727 else:
2727 else:
2728 newpath, i = lastsavename(q.path)
2728 newpath, i = lastsavename(q.path)
2729 if not newpath:
2729 if not newpath:
2730 ui.warn(_("no saved queues found, please use -n\n"))
2730 ui.warn(_("no saved queues found, please use -n\n"))
2731 return 1
2731 return 1
2732 mergeq = queue(ui, repo.path, newpath)
2732 mergeq = queue(ui, repo.path, newpath)
2733 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2733 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2734 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2734 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2735 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2735 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2736 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2736 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2737 keepchanges=opts.get('keep_changes'))
2737 keepchanges=opts.get('keep_changes'))
2738 return ret
2738 return ret
2739
2739
2740 @command("^qpop",
2740 @command("^qpop",
2741 [('a', 'all', None, _('pop all patches')),
2741 [('a', 'all', None, _('pop all patches')),
2742 ('n', 'name', '',
2742 ('n', 'name', '',
2743 _('queue name to pop (DEPRECATED)'), _('NAME')),
2743 _('queue name to pop (DEPRECATED)'), _('NAME')),
2744 ('', 'keep-changes', None,
2744 ('', 'keep-changes', None,
2745 _('tolerate non-conflicting local changes')),
2745 _('tolerate non-conflicting local changes')),
2746 ('f', 'force', None, _('forget any local changes to patched files')),
2746 ('f', 'force', None, _('forget any local changes to patched files')),
2747 ('', 'no-backup', None, _('do not save backup copies of files'))],
2747 ('', 'no-backup', None, _('do not save backup copies of files'))],
2748 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2748 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2749 def pop(ui, repo, patch=None, **opts):
2749 def pop(ui, repo, patch=None, **opts):
2750 """pop the current patch off the stack
2750 """pop the current patch off the stack
2751
2751
2752 Without argument, pops off the top of the patch stack. If given a
2752 Without argument, pops off the top of the patch stack. If given a
2753 patch name, keeps popping off patches until the named patch is at
2753 patch name, keeps popping off patches until the named patch is at
2754 the top of the stack.
2754 the top of the stack.
2755
2755
2756 By default, abort if the working directory contains uncommitted
2756 By default, abort if the working directory contains uncommitted
2757 changes. With --keep-changes, abort only if the uncommitted files
2757 changes. With --keep-changes, abort only if the uncommitted files
2758 overlap with patched files. With -f/--force, backup and discard
2758 overlap with patched files. With -f/--force, backup and discard
2759 changes made to such files.
2759 changes made to such files.
2760
2760
2761 Return 0 on success.
2761 Return 0 on success.
2762 """
2762 """
2763 opts = fixkeepchangesopts(ui, opts)
2763 opts = fixkeepchangesopts(ui, opts)
2764 localupdate = True
2764 localupdate = True
2765 if opts.get('name'):
2765 if opts.get('name'):
2766 q = queue(ui, repo.path, repo.join(opts.get('name')))
2766 q = queue(ui, repo.path, repo.join(opts.get('name')))
2767 ui.warn(_('using patch queue: %s\n') % q.path)
2767 ui.warn(_('using patch queue: %s\n') % q.path)
2768 localupdate = False
2768 localupdate = False
2769 else:
2769 else:
2770 q = repo.mq
2770 q = repo.mq
2771 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2771 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2772 all=opts.get('all'), nobackup=opts.get('no_backup'),
2772 all=opts.get('all'), nobackup=opts.get('no_backup'),
2773 keepchanges=opts.get('keep_changes'))
2773 keepchanges=opts.get('keep_changes'))
2774 q.savedirty()
2774 q.savedirty()
2775 return ret
2775 return ret
2776
2776
2777 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2777 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2778 def rename(ui, repo, patch, name=None, **opts):
2778 def rename(ui, repo, patch, name=None, **opts):
2779 """rename a patch
2779 """rename a patch
2780
2780
2781 With one argument, renames the current patch to PATCH1.
2781 With one argument, renames the current patch to PATCH1.
2782 With two arguments, renames PATCH1 to PATCH2.
2782 With two arguments, renames PATCH1 to PATCH2.
2783
2783
2784 Returns 0 on success."""
2784 Returns 0 on success."""
2785 q = repo.mq
2785 q = repo.mq
2786 if not name:
2786 if not name:
2787 name = patch
2787 name = patch
2788 patch = None
2788 patch = None
2789
2789
2790 if patch:
2790 if patch:
2791 patch = q.lookup(patch)
2791 patch = q.lookup(patch)
2792 else:
2792 else:
2793 if not q.applied:
2793 if not q.applied:
2794 ui.write(_('no patches applied\n'))
2794 ui.write(_('no patches applied\n'))
2795 return
2795 return
2796 patch = q.lookup('qtip')
2796 patch = q.lookup('qtip')
2797 absdest = q.join(name)
2797 absdest = q.join(name)
2798 if os.path.isdir(absdest):
2798 if os.path.isdir(absdest):
2799 name = normname(os.path.join(name, os.path.basename(patch)))
2799 name = normname(os.path.join(name, os.path.basename(patch)))
2800 absdest = q.join(name)
2800 absdest = q.join(name)
2801 q.checkpatchname(name)
2801 q.checkpatchname(name)
2802
2802
2803 ui.note(_('renaming %s to %s\n') % (patch, name))
2803 ui.note(_('renaming %s to %s\n') % (patch, name))
2804 i = q.findseries(patch)
2804 i = q.findseries(patch)
2805 guards = q.guard_re.findall(q.fullseries[i])
2805 guards = q.guard_re.findall(q.fullseries[i])
2806 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2806 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2807 q.parseseries()
2807 q.parseseries()
2808 q.seriesdirty = True
2808 q.seriesdirty = True
2809
2809
2810 info = q.isapplied(patch)
2810 info = q.isapplied(patch)
2811 if info:
2811 if info:
2812 q.applied[info[0]] = statusentry(info[1], name)
2812 q.applied[info[0]] = statusentry(info[1], name)
2813 q.applieddirty = True
2813 q.applieddirty = True
2814
2814
2815 destdir = os.path.dirname(absdest)
2815 destdir = os.path.dirname(absdest)
2816 if not os.path.isdir(destdir):
2816 if not os.path.isdir(destdir):
2817 os.makedirs(destdir)
2817 os.makedirs(destdir)
2818 util.rename(q.join(patch), absdest)
2818 util.rename(q.join(patch), absdest)
2819 r = q.qrepo()
2819 r = q.qrepo()
2820 if r and patch in r.dirstate:
2820 if r and patch in r.dirstate:
2821 wctx = r[None]
2821 wctx = r[None]
2822 wlock = r.wlock()
2822 wlock = r.wlock()
2823 try:
2823 try:
2824 if r.dirstate[patch] == 'a':
2824 if r.dirstate[patch] == 'a':
2825 r.dirstate.drop(patch)
2825 r.dirstate.drop(patch)
2826 r.dirstate.add(name)
2826 r.dirstate.add(name)
2827 else:
2827 else:
2828 wctx.copy(patch, name)
2828 wctx.copy(patch, name)
2829 wctx.forget([patch])
2829 wctx.forget([patch])
2830 finally:
2830 finally:
2831 wlock.release()
2831 wlock.release()
2832
2832
2833 q.savedirty()
2833 q.savedirty()
2834
2834
2835 @command("qrestore",
2835 @command("qrestore",
2836 [('d', 'delete', None, _('delete save entry')),
2836 [('d', 'delete', None, _('delete save entry')),
2837 ('u', 'update', None, _('update queue working directory'))],
2837 ('u', 'update', None, _('update queue working directory'))],
2838 _('hg qrestore [-d] [-u] REV'))
2838 _('hg qrestore [-d] [-u] REV'))
2839 def restore(ui, repo, rev, **opts):
2839 def restore(ui, repo, rev, **opts):
2840 """restore the queue state saved by a revision (DEPRECATED)
2840 """restore the queue state saved by a revision (DEPRECATED)
2841
2841
2842 This command is deprecated, use :hg:`rebase` instead."""
2842 This command is deprecated, use :hg:`rebase` instead."""
2843 rev = repo.lookup(rev)
2843 rev = repo.lookup(rev)
2844 q = repo.mq
2844 q = repo.mq
2845 q.restore(repo, rev, delete=opts.get('delete'),
2845 q.restore(repo, rev, delete=opts.get('delete'),
2846 qupdate=opts.get('update'))
2846 qupdate=opts.get('update'))
2847 q.savedirty()
2847 q.savedirty()
2848 return 0
2848 return 0
2849
2849
2850 @command("qsave",
2850 @command("qsave",
2851 [('c', 'copy', None, _('copy patch directory')),
2851 [('c', 'copy', None, _('copy patch directory')),
2852 ('n', 'name', '',
2852 ('n', 'name', '',
2853 _('copy directory name'), _('NAME')),
2853 _('copy directory name'), _('NAME')),
2854 ('e', 'empty', None, _('clear queue status file')),
2854 ('e', 'empty', None, _('clear queue status file')),
2855 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2855 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2856 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
2856 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
2857 def save(ui, repo, **opts):
2857 def save(ui, repo, **opts):
2858 """save current queue state (DEPRECATED)
2858 """save current queue state (DEPRECATED)
2859
2859
2860 This command is deprecated, use :hg:`rebase` instead."""
2860 This command is deprecated, use :hg:`rebase` instead."""
2861 q = repo.mq
2861 q = repo.mq
2862 message = cmdutil.logmessage(ui, opts)
2862 message = cmdutil.logmessage(ui, opts)
2863 ret = q.save(repo, msg=message)
2863 ret = q.save(repo, msg=message)
2864 if ret:
2864 if ret:
2865 return ret
2865 return ret
2866 q.savedirty() # save to .hg/patches before copying
2866 q.savedirty() # save to .hg/patches before copying
2867 if opts.get('copy'):
2867 if opts.get('copy'):
2868 path = q.path
2868 path = q.path
2869 if opts.get('name'):
2869 if opts.get('name'):
2870 newpath = os.path.join(q.basepath, opts.get('name'))
2870 newpath = os.path.join(q.basepath, opts.get('name'))
2871 if os.path.exists(newpath):
2871 if os.path.exists(newpath):
2872 if not os.path.isdir(newpath):
2872 if not os.path.isdir(newpath):
2873 raise util.Abort(_('destination %s exists and is not '
2873 raise util.Abort(_('destination %s exists and is not '
2874 'a directory') % newpath)
2874 'a directory') % newpath)
2875 if not opts.get('force'):
2875 if not opts.get('force'):
2876 raise util.Abort(_('destination %s exists, '
2876 raise util.Abort(_('destination %s exists, '
2877 'use -f to force') % newpath)
2877 'use -f to force') % newpath)
2878 else:
2878 else:
2879 newpath = savename(path)
2879 newpath = savename(path)
2880 ui.warn(_("copy %s to %s\n") % (path, newpath))
2880 ui.warn(_("copy %s to %s\n") % (path, newpath))
2881 util.copyfiles(path, newpath)
2881 util.copyfiles(path, newpath)
2882 if opts.get('empty'):
2882 if opts.get('empty'):
2883 del q.applied[:]
2883 del q.applied[:]
2884 q.applieddirty = True
2884 q.applieddirty = True
2885 q.savedirty()
2885 q.savedirty()
2886 return 0
2886 return 0
2887
2887
2888 @command("strip",
2888 @command("strip",
2889 [
2889 [
2890 ('r', 'rev', [], _('strip specified revision (optional, '
2890 ('r', 'rev', [], _('strip specified revision (optional, '
2891 'can specify revisions without this '
2891 'can specify revisions without this '
2892 'option)'), _('REV')),
2892 'option)'), _('REV')),
2893 ('f', 'force', None, _('force removal of changesets, discard '
2893 ('f', 'force', None, _('force removal of changesets, discard '
2894 'uncommitted changes (no backup)')),
2894 'uncommitted changes (no backup)')),
2895 ('b', 'backup', None, _('bundle only changesets with local revision'
2895 ('b', 'backup', None, _('bundle only changesets with local revision'
2896 ' number greater than REV which are not'
2896 ' number greater than REV which are not'
2897 ' descendants of REV (DEPRECATED)')),
2897 ' descendants of REV (DEPRECATED)')),
2898 ('', 'no-backup', None, _('no backups')),
2898 ('', 'no-backup', None, _('no backups')),
2899 ('', 'nobackup', None, _('no backups (DEPRECATED)')),
2899 ('', 'nobackup', None, _('no backups (DEPRECATED)')),
2900 ('n', '', None, _('ignored (DEPRECATED)')),
2900 ('n', '', None, _('ignored (DEPRECATED)')),
2901 ('k', 'keep', None, _("do not modify working copy during strip")),
2901 ('k', 'keep', None, _("do not modify working copy during strip")),
2902 ('B', 'bookmark', '', _("remove revs only reachable from given"
2902 ('B', 'bookmark', '', _("remove revs only reachable from given"
2903 " bookmark"))],
2903 " bookmark"))],
2904 _('hg strip [-k] [-f] [-n] [-B bookmark] REV...'))
2904 _('hg strip [-k] [-f] [-n] [-B bookmark] [-r] REV...'))
2905 def strip(ui, repo, *revs, **opts):
2905 def strip(ui, repo, *revs, **opts):
2906 """strip changesets and all their descendants from the repository
2906 """strip changesets and all their descendants from the repository
2907
2907
2908 The strip command removes the specified changesets and all their
2908 The strip command removes the specified changesets and all their
2909 descendants. If the working directory has uncommitted changes, the
2909 descendants. If the working directory has uncommitted changes, the
2910 operation is aborted unless the --force flag is supplied, in which
2910 operation is aborted unless the --force flag is supplied, in which
2911 case changes will be discarded.
2911 case changes will be discarded.
2912
2912
2913 If a parent of the working directory is stripped, then the working
2913 If a parent of the working directory is stripped, then the working
2914 directory will automatically be updated to the most recent
2914 directory will automatically be updated to the most recent
2915 available ancestor of the stripped parent after the operation
2915 available ancestor of the stripped parent after the operation
2916 completes.
2916 completes.
2917
2917
2918 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2918 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2919 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2919 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2920 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2920 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2921 where BUNDLE is the bundle file created by the strip. Note that
2921 where BUNDLE is the bundle file created by the strip. Note that
2922 the local revision numbers will in general be different after the
2922 the local revision numbers will in general be different after the
2923 restore.
2923 restore.
2924
2924
2925 Use the --no-backup option to discard the backup bundle once the
2925 Use the --no-backup option to discard the backup bundle once the
2926 operation completes.
2926 operation completes.
2927
2927
2928 Return 0 on success.
2928 Return 0 on success.
2929 """
2929 """
2930 backup = 'all'
2930 backup = 'all'
2931 if opts.get('backup'):
2931 if opts.get('backup'):
2932 backup = 'strip'
2932 backup = 'strip'
2933 elif opts.get('no_backup') or opts.get('nobackup'):
2933 elif opts.get('no_backup') or opts.get('nobackup'):
2934 backup = 'none'
2934 backup = 'none'
2935
2935
2936 cl = repo.changelog
2936 cl = repo.changelog
2937 revs = list(revs) + opts.get('rev')
2937 revs = list(revs) + opts.get('rev')
2938 revs = set(scmutil.revrange(repo, revs))
2938 revs = set(scmutil.revrange(repo, revs))
2939
2939
2940 if opts.get('bookmark'):
2940 if opts.get('bookmark'):
2941 mark = opts.get('bookmark')
2941 mark = opts.get('bookmark')
2942 marks = repo._bookmarks
2942 marks = repo._bookmarks
2943 if mark not in marks:
2943 if mark not in marks:
2944 raise util.Abort(_("bookmark '%s' not found") % mark)
2944 raise util.Abort(_("bookmark '%s' not found") % mark)
2945
2945
2946 # If the requested bookmark is not the only one pointing to a
2946 # If the requested bookmark is not the only one pointing to a
2947 # a revision we have to only delete the bookmark and not strip
2947 # a revision we have to only delete the bookmark and not strip
2948 # anything. revsets cannot detect that case.
2948 # anything. revsets cannot detect that case.
2949 uniquebm = True
2949 uniquebm = True
2950 for m, n in marks.iteritems():
2950 for m, n in marks.iteritems():
2951 if m != mark and n == repo[mark].node():
2951 if m != mark and n == repo[mark].node():
2952 uniquebm = False
2952 uniquebm = False
2953 break
2953 break
2954 if uniquebm:
2954 if uniquebm:
2955 rsrevs = repo.revs("ancestors(bookmark(%s)) - "
2955 rsrevs = repo.revs("ancestors(bookmark(%s)) - "
2956 "ancestors(head() and not bookmark(%s)) - "
2956 "ancestors(head() and not bookmark(%s)) - "
2957 "ancestors(bookmark() and not bookmark(%s))",
2957 "ancestors(bookmark() and not bookmark(%s))",
2958 mark, mark, mark)
2958 mark, mark, mark)
2959 revs.update(set(rsrevs))
2959 revs.update(set(rsrevs))
2960 if not revs:
2960 if not revs:
2961 del marks[mark]
2961 del marks[mark]
2962 repo._writebookmarks(mark)
2962 repo._writebookmarks(mark)
2963 ui.write(_("bookmark '%s' deleted\n") % mark)
2963 ui.write(_("bookmark '%s' deleted\n") % mark)
2964
2964
2965 if not revs:
2965 if not revs:
2966 raise util.Abort(_('empty revision set'))
2966 raise util.Abort(_('empty revision set'))
2967
2967
2968 descendants = set(cl.descendants(revs))
2968 descendants = set(cl.descendants(revs))
2969 strippedrevs = revs.union(descendants)
2969 strippedrevs = revs.union(descendants)
2970 roots = revs.difference(descendants)
2970 roots = revs.difference(descendants)
2971
2971
2972 update = False
2972 update = False
2973 # if one of the wdir parent is stripped we'll need
2973 # if one of the wdir parent is stripped we'll need
2974 # to update away to an earlier revision
2974 # to update away to an earlier revision
2975 for p in repo.dirstate.parents():
2975 for p in repo.dirstate.parents():
2976 if p != nullid and cl.rev(p) in strippedrevs:
2976 if p != nullid and cl.rev(p) in strippedrevs:
2977 update = True
2977 update = True
2978 break
2978 break
2979
2979
2980 rootnodes = set(cl.node(r) for r in roots)
2980 rootnodes = set(cl.node(r) for r in roots)
2981
2981
2982 q = repo.mq
2982 q = repo.mq
2983 if q.applied:
2983 if q.applied:
2984 # refresh queue state if we're about to strip
2984 # refresh queue state if we're about to strip
2985 # applied patches
2985 # applied patches
2986 if cl.rev(repo.lookup('qtip')) in strippedrevs:
2986 if cl.rev(repo.lookup('qtip')) in strippedrevs:
2987 q.applieddirty = True
2987 q.applieddirty = True
2988 start = 0
2988 start = 0
2989 end = len(q.applied)
2989 end = len(q.applied)
2990 for i, statusentry in enumerate(q.applied):
2990 for i, statusentry in enumerate(q.applied):
2991 if statusentry.node in rootnodes:
2991 if statusentry.node in rootnodes:
2992 # if one of the stripped roots is an applied
2992 # if one of the stripped roots is an applied
2993 # patch, only part of the queue is stripped
2993 # patch, only part of the queue is stripped
2994 start = i
2994 start = i
2995 break
2995 break
2996 del q.applied[start:end]
2996 del q.applied[start:end]
2997 q.savedirty()
2997 q.savedirty()
2998
2998
2999 revs = list(rootnodes)
2999 revs = list(rootnodes)
3000 if update and opts.get('keep'):
3000 if update and opts.get('keep'):
3001 wlock = repo.wlock()
3001 wlock = repo.wlock()
3002 try:
3002 try:
3003 urev = repo.mq.qparents(repo, revs[0])
3003 urev = repo.mq.qparents(repo, revs[0])
3004 repo.dirstate.rebuild(urev, repo[urev].manifest())
3004 repo.dirstate.rebuild(urev, repo[urev].manifest())
3005 repo.dirstate.write()
3005 repo.dirstate.write()
3006 update = False
3006 update = False
3007 finally:
3007 finally:
3008 wlock.release()
3008 wlock.release()
3009
3009
3010 if opts.get('bookmark'):
3010 if opts.get('bookmark'):
3011 del marks[mark]
3011 del marks[mark]
3012 repo._writebookmarks(marks)
3012 repo._writebookmarks(marks)
3013 ui.write(_("bookmark '%s' deleted\n") % mark)
3013 ui.write(_("bookmark '%s' deleted\n") % mark)
3014
3014
3015 repo.mq.strip(repo, revs, backup=backup, update=update,
3015 repo.mq.strip(repo, revs, backup=backup, update=update,
3016 force=opts.get('force'))
3016 force=opts.get('force'))
3017
3017
3018 return 0
3018 return 0
3019
3019
3020 @command("qselect",
3020 @command("qselect",
3021 [('n', 'none', None, _('disable all guards')),
3021 [('n', 'none', None, _('disable all guards')),
3022 ('s', 'series', None, _('list all guards in series file')),
3022 ('s', 'series', None, _('list all guards in series file')),
3023 ('', 'pop', None, _('pop to before first guarded applied patch')),
3023 ('', 'pop', None, _('pop to before first guarded applied patch')),
3024 ('', 'reapply', None, _('pop, then reapply patches'))],
3024 ('', 'reapply', None, _('pop, then reapply patches'))],
3025 _('hg qselect [OPTION]... [GUARD]...'))
3025 _('hg qselect [OPTION]... [GUARD]...'))
3026 def select(ui, repo, *args, **opts):
3026 def select(ui, repo, *args, **opts):
3027 '''set or print guarded patches to push
3027 '''set or print guarded patches to push
3028
3028
3029 Use the :hg:`qguard` command to set or print guards on patch, then use
3029 Use the :hg:`qguard` command to set or print guards on patch, then use
3030 qselect to tell mq which guards to use. A patch will be pushed if
3030 qselect to tell mq which guards to use. A patch will be pushed if
3031 it has no guards or any positive guards match the currently
3031 it has no guards or any positive guards match the currently
3032 selected guard, but will not be pushed if any negative guards
3032 selected guard, but will not be pushed if any negative guards
3033 match the current guard. For example::
3033 match the current guard. For example::
3034
3034
3035 qguard foo.patch -- -stable (negative guard)
3035 qguard foo.patch -- -stable (negative guard)
3036 qguard bar.patch +stable (positive guard)
3036 qguard bar.patch +stable (positive guard)
3037 qselect stable
3037 qselect stable
3038
3038
3039 This activates the "stable" guard. mq will skip foo.patch (because
3039 This activates the "stable" guard. mq will skip foo.patch (because
3040 it has a negative match) but push bar.patch (because it has a
3040 it has a negative match) but push bar.patch (because it has a
3041 positive match).
3041 positive match).
3042
3042
3043 With no arguments, prints the currently active guards.
3043 With no arguments, prints the currently active guards.
3044 With one argument, sets the active guard.
3044 With one argument, sets the active guard.
3045
3045
3046 Use -n/--none to deactivate guards (no other arguments needed).
3046 Use -n/--none to deactivate guards (no other arguments needed).
3047 When no guards are active, patches with positive guards are
3047 When no guards are active, patches with positive guards are
3048 skipped and patches with negative guards are pushed.
3048 skipped and patches with negative guards are pushed.
3049
3049
3050 qselect can change the guards on applied patches. It does not pop
3050 qselect can change the guards on applied patches. It does not pop
3051 guarded patches by default. Use --pop to pop back to the last
3051 guarded patches by default. Use --pop to pop back to the last
3052 applied patch that is not guarded. Use --reapply (which implies
3052 applied patch that is not guarded. Use --reapply (which implies
3053 --pop) to push back to the current patch afterwards, but skip
3053 --pop) to push back to the current patch afterwards, but skip
3054 guarded patches.
3054 guarded patches.
3055
3055
3056 Use -s/--series to print a list of all guards in the series file
3056 Use -s/--series to print a list of all guards in the series file
3057 (no other arguments needed). Use -v for more information.
3057 (no other arguments needed). Use -v for more information.
3058
3058
3059 Returns 0 on success.'''
3059 Returns 0 on success.'''
3060
3060
3061 q = repo.mq
3061 q = repo.mq
3062 guards = q.active()
3062 guards = q.active()
3063 if args or opts.get('none'):
3063 if args or opts.get('none'):
3064 old_unapplied = q.unapplied(repo)
3064 old_unapplied = q.unapplied(repo)
3065 old_guarded = [i for i in xrange(len(q.applied)) if
3065 old_guarded = [i for i in xrange(len(q.applied)) if
3066 not q.pushable(i)[0]]
3066 not q.pushable(i)[0]]
3067 q.setactive(args)
3067 q.setactive(args)
3068 q.savedirty()
3068 q.savedirty()
3069 if not args:
3069 if not args:
3070 ui.status(_('guards deactivated\n'))
3070 ui.status(_('guards deactivated\n'))
3071 if not opts.get('pop') and not opts.get('reapply'):
3071 if not opts.get('pop') and not opts.get('reapply'):
3072 unapplied = q.unapplied(repo)
3072 unapplied = q.unapplied(repo)
3073 guarded = [i for i in xrange(len(q.applied))
3073 guarded = [i for i in xrange(len(q.applied))
3074 if not q.pushable(i)[0]]
3074 if not q.pushable(i)[0]]
3075 if len(unapplied) != len(old_unapplied):
3075 if len(unapplied) != len(old_unapplied):
3076 ui.status(_('number of unguarded, unapplied patches has '
3076 ui.status(_('number of unguarded, unapplied patches has '
3077 'changed from %d to %d\n') %
3077 'changed from %d to %d\n') %
3078 (len(old_unapplied), len(unapplied)))
3078 (len(old_unapplied), len(unapplied)))
3079 if len(guarded) != len(old_guarded):
3079 if len(guarded) != len(old_guarded):
3080 ui.status(_('number of guarded, applied patches has changed '
3080 ui.status(_('number of guarded, applied patches has changed '
3081 'from %d to %d\n') %
3081 'from %d to %d\n') %
3082 (len(old_guarded), len(guarded)))
3082 (len(old_guarded), len(guarded)))
3083 elif opts.get('series'):
3083 elif opts.get('series'):
3084 guards = {}
3084 guards = {}
3085 noguards = 0
3085 noguards = 0
3086 for gs in q.seriesguards:
3086 for gs in q.seriesguards:
3087 if not gs:
3087 if not gs:
3088 noguards += 1
3088 noguards += 1
3089 for g in gs:
3089 for g in gs:
3090 guards.setdefault(g, 0)
3090 guards.setdefault(g, 0)
3091 guards[g] += 1
3091 guards[g] += 1
3092 if ui.verbose:
3092 if ui.verbose:
3093 guards['NONE'] = noguards
3093 guards['NONE'] = noguards
3094 guards = guards.items()
3094 guards = guards.items()
3095 guards.sort(key=lambda x: x[0][1:])
3095 guards.sort(key=lambda x: x[0][1:])
3096 if guards:
3096 if guards:
3097 ui.note(_('guards in series file:\n'))
3097 ui.note(_('guards in series file:\n'))
3098 for guard, count in guards:
3098 for guard, count in guards:
3099 ui.note('%2d ' % count)
3099 ui.note('%2d ' % count)
3100 ui.write(guard, '\n')
3100 ui.write(guard, '\n')
3101 else:
3101 else:
3102 ui.note(_('no guards in series file\n'))
3102 ui.note(_('no guards in series file\n'))
3103 else:
3103 else:
3104 if guards:
3104 if guards:
3105 ui.note(_('active guards:\n'))
3105 ui.note(_('active guards:\n'))
3106 for g in guards:
3106 for g in guards:
3107 ui.write(g, '\n')
3107 ui.write(g, '\n')
3108 else:
3108 else:
3109 ui.write(_('no active guards\n'))
3109 ui.write(_('no active guards\n'))
3110 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
3110 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
3111 popped = False
3111 popped = False
3112 if opts.get('pop') or opts.get('reapply'):
3112 if opts.get('pop') or opts.get('reapply'):
3113 for i in xrange(len(q.applied)):
3113 for i in xrange(len(q.applied)):
3114 pushable, reason = q.pushable(i)
3114 pushable, reason = q.pushable(i)
3115 if not pushable:
3115 if not pushable:
3116 ui.status(_('popping guarded patches\n'))
3116 ui.status(_('popping guarded patches\n'))
3117 popped = True
3117 popped = True
3118 if i == 0:
3118 if i == 0:
3119 q.pop(repo, all=True)
3119 q.pop(repo, all=True)
3120 else:
3120 else:
3121 q.pop(repo, str(i - 1))
3121 q.pop(repo, str(i - 1))
3122 break
3122 break
3123 if popped:
3123 if popped:
3124 try:
3124 try:
3125 if reapply:
3125 if reapply:
3126 ui.status(_('reapplying unguarded patches\n'))
3126 ui.status(_('reapplying unguarded patches\n'))
3127 q.push(repo, reapply)
3127 q.push(repo, reapply)
3128 finally:
3128 finally:
3129 q.savedirty()
3129 q.savedirty()
3130
3130
3131 @command("qfinish",
3131 @command("qfinish",
3132 [('a', 'applied', None, _('finish all applied changesets'))],
3132 [('a', 'applied', None, _('finish all applied changesets'))],
3133 _('hg qfinish [-a] [REV]...'))
3133 _('hg qfinish [-a] [REV]...'))
3134 def finish(ui, repo, *revrange, **opts):
3134 def finish(ui, repo, *revrange, **opts):
3135 """move applied patches into repository history
3135 """move applied patches into repository history
3136
3136
3137 Finishes the specified revisions (corresponding to applied
3137 Finishes the specified revisions (corresponding to applied
3138 patches) by moving them out of mq control into regular repository
3138 patches) by moving them out of mq control into regular repository
3139 history.
3139 history.
3140
3140
3141 Accepts a revision range or the -a/--applied option. If --applied
3141 Accepts a revision range or the -a/--applied option. If --applied
3142 is specified, all applied mq revisions are removed from mq
3142 is specified, all applied mq revisions are removed from mq
3143 control. Otherwise, the given revisions must be at the base of the
3143 control. Otherwise, the given revisions must be at the base of the
3144 stack of applied patches.
3144 stack of applied patches.
3145
3145
3146 This can be especially useful if your changes have been applied to
3146 This can be especially useful if your changes have been applied to
3147 an upstream repository, or if you are about to push your changes
3147 an upstream repository, or if you are about to push your changes
3148 to upstream.
3148 to upstream.
3149
3149
3150 Returns 0 on success.
3150 Returns 0 on success.
3151 """
3151 """
3152 if not opts.get('applied') and not revrange:
3152 if not opts.get('applied') and not revrange:
3153 raise util.Abort(_('no revisions specified'))
3153 raise util.Abort(_('no revisions specified'))
3154 elif opts.get('applied'):
3154 elif opts.get('applied'):
3155 revrange = ('qbase::qtip',) + revrange
3155 revrange = ('qbase::qtip',) + revrange
3156
3156
3157 q = repo.mq
3157 q = repo.mq
3158 if not q.applied:
3158 if not q.applied:
3159 ui.status(_('no patches applied\n'))
3159 ui.status(_('no patches applied\n'))
3160 return 0
3160 return 0
3161
3161
3162 revs = scmutil.revrange(repo, revrange)
3162 revs = scmutil.revrange(repo, revrange)
3163 if repo['.'].rev() in revs and repo[None].files():
3163 if repo['.'].rev() in revs and repo[None].files():
3164 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3164 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3165 # queue.finish may changes phases but leave the responsability to lock the
3165 # queue.finish may changes phases but leave the responsability to lock the
3166 # repo to the caller to avoid deadlock with wlock. This command code is
3166 # repo to the caller to avoid deadlock with wlock. This command code is
3167 # responsability for this locking.
3167 # responsability for this locking.
3168 lock = repo.lock()
3168 lock = repo.lock()
3169 try:
3169 try:
3170 q.finish(repo, revs)
3170 q.finish(repo, revs)
3171 q.savedirty()
3171 q.savedirty()
3172 finally:
3172 finally:
3173 lock.release()
3173 lock.release()
3174 return 0
3174 return 0
3175
3175
3176 @command("qqueue",
3176 @command("qqueue",
3177 [('l', 'list', False, _('list all available queues')),
3177 [('l', 'list', False, _('list all available queues')),
3178 ('', 'active', False, _('print name of active queue')),
3178 ('', 'active', False, _('print name of active queue')),
3179 ('c', 'create', False, _('create new queue')),
3179 ('c', 'create', False, _('create new queue')),
3180 ('', 'rename', False, _('rename active queue')),
3180 ('', 'rename', False, _('rename active queue')),
3181 ('', 'delete', False, _('delete reference to queue')),
3181 ('', 'delete', False, _('delete reference to queue')),
3182 ('', 'purge', False, _('delete queue, and remove patch dir')),
3182 ('', 'purge', False, _('delete queue, and remove patch dir')),
3183 ],
3183 ],
3184 _('[OPTION] [QUEUE]'))
3184 _('[OPTION] [QUEUE]'))
3185 def qqueue(ui, repo, name=None, **opts):
3185 def qqueue(ui, repo, name=None, **opts):
3186 '''manage multiple patch queues
3186 '''manage multiple patch queues
3187
3187
3188 Supports switching between different patch queues, as well as creating
3188 Supports switching between different patch queues, as well as creating
3189 new patch queues and deleting existing ones.
3189 new patch queues and deleting existing ones.
3190
3190
3191 Omitting a queue name or specifying -l/--list will show you the registered
3191 Omitting a queue name or specifying -l/--list will show you the registered
3192 queues - by default the "normal" patches queue is registered. The currently
3192 queues - by default the "normal" patches queue is registered. The currently
3193 active queue will be marked with "(active)". Specifying --active will print
3193 active queue will be marked with "(active)". Specifying --active will print
3194 only the name of the active queue.
3194 only the name of the active queue.
3195
3195
3196 To create a new queue, use -c/--create. The queue is automatically made
3196 To create a new queue, use -c/--create. The queue is automatically made
3197 active, except in the case where there are applied patches from the
3197 active, except in the case where there are applied patches from the
3198 currently active queue in the repository. Then the queue will only be
3198 currently active queue in the repository. Then the queue will only be
3199 created and switching will fail.
3199 created and switching will fail.
3200
3200
3201 To delete an existing queue, use --delete. You cannot delete the currently
3201 To delete an existing queue, use --delete. You cannot delete the currently
3202 active queue.
3202 active queue.
3203
3203
3204 Returns 0 on success.
3204 Returns 0 on success.
3205 '''
3205 '''
3206 q = repo.mq
3206 q = repo.mq
3207 _defaultqueue = 'patches'
3207 _defaultqueue = 'patches'
3208 _allqueues = 'patches.queues'
3208 _allqueues = 'patches.queues'
3209 _activequeue = 'patches.queue'
3209 _activequeue = 'patches.queue'
3210
3210
3211 def _getcurrent():
3211 def _getcurrent():
3212 cur = os.path.basename(q.path)
3212 cur = os.path.basename(q.path)
3213 if cur.startswith('patches-'):
3213 if cur.startswith('patches-'):
3214 cur = cur[8:]
3214 cur = cur[8:]
3215 return cur
3215 return cur
3216
3216
3217 def _noqueues():
3217 def _noqueues():
3218 try:
3218 try:
3219 fh = repo.opener(_allqueues, 'r')
3219 fh = repo.opener(_allqueues, 'r')
3220 fh.close()
3220 fh.close()
3221 except IOError:
3221 except IOError:
3222 return True
3222 return True
3223
3223
3224 return False
3224 return False
3225
3225
3226 def _getqueues():
3226 def _getqueues():
3227 current = _getcurrent()
3227 current = _getcurrent()
3228
3228
3229 try:
3229 try:
3230 fh = repo.opener(_allqueues, 'r')
3230 fh = repo.opener(_allqueues, 'r')
3231 queues = [queue.strip() for queue in fh if queue.strip()]
3231 queues = [queue.strip() for queue in fh if queue.strip()]
3232 fh.close()
3232 fh.close()
3233 if current not in queues:
3233 if current not in queues:
3234 queues.append(current)
3234 queues.append(current)
3235 except IOError:
3235 except IOError:
3236 queues = [_defaultqueue]
3236 queues = [_defaultqueue]
3237
3237
3238 return sorted(queues)
3238 return sorted(queues)
3239
3239
3240 def _setactive(name):
3240 def _setactive(name):
3241 if q.applied:
3241 if q.applied:
3242 raise util.Abort(_('patches applied - cannot set new queue active'))
3242 raise util.Abort(_('patches applied - cannot set new queue active'))
3243 _setactivenocheck(name)
3243 _setactivenocheck(name)
3244
3244
3245 def _setactivenocheck(name):
3245 def _setactivenocheck(name):
3246 fh = repo.opener(_activequeue, 'w')
3246 fh = repo.opener(_activequeue, 'w')
3247 if name != 'patches':
3247 if name != 'patches':
3248 fh.write(name)
3248 fh.write(name)
3249 fh.close()
3249 fh.close()
3250
3250
3251 def _addqueue(name):
3251 def _addqueue(name):
3252 fh = repo.opener(_allqueues, 'a')
3252 fh = repo.opener(_allqueues, 'a')
3253 fh.write('%s\n' % (name,))
3253 fh.write('%s\n' % (name,))
3254 fh.close()
3254 fh.close()
3255
3255
3256 def _queuedir(name):
3256 def _queuedir(name):
3257 if name == 'patches':
3257 if name == 'patches':
3258 return repo.join('patches')
3258 return repo.join('patches')
3259 else:
3259 else:
3260 return repo.join('patches-' + name)
3260 return repo.join('patches-' + name)
3261
3261
3262 def _validname(name):
3262 def _validname(name):
3263 for n in name:
3263 for n in name:
3264 if n in ':\\/.':
3264 if n in ':\\/.':
3265 return False
3265 return False
3266 return True
3266 return True
3267
3267
3268 def _delete(name):
3268 def _delete(name):
3269 if name not in existing:
3269 if name not in existing:
3270 raise util.Abort(_('cannot delete queue that does not exist'))
3270 raise util.Abort(_('cannot delete queue that does not exist'))
3271
3271
3272 current = _getcurrent()
3272 current = _getcurrent()
3273
3273
3274 if name == current:
3274 if name == current:
3275 raise util.Abort(_('cannot delete currently active queue'))
3275 raise util.Abort(_('cannot delete currently active queue'))
3276
3276
3277 fh = repo.opener('patches.queues.new', 'w')
3277 fh = repo.opener('patches.queues.new', 'w')
3278 for queue in existing:
3278 for queue in existing:
3279 if queue == name:
3279 if queue == name:
3280 continue
3280 continue
3281 fh.write('%s\n' % (queue,))
3281 fh.write('%s\n' % (queue,))
3282 fh.close()
3282 fh.close()
3283 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3283 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3284
3284
3285 if not name or opts.get('list') or opts.get('active'):
3285 if not name or opts.get('list') or opts.get('active'):
3286 current = _getcurrent()
3286 current = _getcurrent()
3287 if opts.get('active'):
3287 if opts.get('active'):
3288 ui.write('%s\n' % (current,))
3288 ui.write('%s\n' % (current,))
3289 return
3289 return
3290 for queue in _getqueues():
3290 for queue in _getqueues():
3291 ui.write('%s' % (queue,))
3291 ui.write('%s' % (queue,))
3292 if queue == current and not ui.quiet:
3292 if queue == current and not ui.quiet:
3293 ui.write(_(' (active)\n'))
3293 ui.write(_(' (active)\n'))
3294 else:
3294 else:
3295 ui.write('\n')
3295 ui.write('\n')
3296 return
3296 return
3297
3297
3298 if not _validname(name):
3298 if not _validname(name):
3299 raise util.Abort(
3299 raise util.Abort(
3300 _('invalid queue name, may not contain the characters ":\\/."'))
3300 _('invalid queue name, may not contain the characters ":\\/."'))
3301
3301
3302 existing = _getqueues()
3302 existing = _getqueues()
3303
3303
3304 if opts.get('create'):
3304 if opts.get('create'):
3305 if name in existing:
3305 if name in existing:
3306 raise util.Abort(_('queue "%s" already exists') % name)
3306 raise util.Abort(_('queue "%s" already exists') % name)
3307 if _noqueues():
3307 if _noqueues():
3308 _addqueue(_defaultqueue)
3308 _addqueue(_defaultqueue)
3309 _addqueue(name)
3309 _addqueue(name)
3310 _setactive(name)
3310 _setactive(name)
3311 elif opts.get('rename'):
3311 elif opts.get('rename'):
3312 current = _getcurrent()
3312 current = _getcurrent()
3313 if name == current:
3313 if name == current:
3314 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
3314 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
3315 if name in existing:
3315 if name in existing:
3316 raise util.Abort(_('queue "%s" already exists') % name)
3316 raise util.Abort(_('queue "%s" already exists') % name)
3317
3317
3318 olddir = _queuedir(current)
3318 olddir = _queuedir(current)
3319 newdir = _queuedir(name)
3319 newdir = _queuedir(name)
3320
3320
3321 if os.path.exists(newdir):
3321 if os.path.exists(newdir):
3322 raise util.Abort(_('non-queue directory "%s" already exists') %
3322 raise util.Abort(_('non-queue directory "%s" already exists') %
3323 newdir)
3323 newdir)
3324
3324
3325 fh = repo.opener('patches.queues.new', 'w')
3325 fh = repo.opener('patches.queues.new', 'w')
3326 for queue in existing:
3326 for queue in existing:
3327 if queue == current:
3327 if queue == current:
3328 fh.write('%s\n' % (name,))
3328 fh.write('%s\n' % (name,))
3329 if os.path.exists(olddir):
3329 if os.path.exists(olddir):
3330 util.rename(olddir, newdir)
3330 util.rename(olddir, newdir)
3331 else:
3331 else:
3332 fh.write('%s\n' % (queue,))
3332 fh.write('%s\n' % (queue,))
3333 fh.close()
3333 fh.close()
3334 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3334 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3335 _setactivenocheck(name)
3335 _setactivenocheck(name)
3336 elif opts.get('delete'):
3336 elif opts.get('delete'):
3337 _delete(name)
3337 _delete(name)
3338 elif opts.get('purge'):
3338 elif opts.get('purge'):
3339 if name in existing:
3339 if name in existing:
3340 _delete(name)
3340 _delete(name)
3341 qdir = _queuedir(name)
3341 qdir = _queuedir(name)
3342 if os.path.exists(qdir):
3342 if os.path.exists(qdir):
3343 shutil.rmtree(qdir)
3343 shutil.rmtree(qdir)
3344 else:
3344 else:
3345 if name not in existing:
3345 if name not in existing:
3346 raise util.Abort(_('use --create to create a new queue'))
3346 raise util.Abort(_('use --create to create a new queue'))
3347 _setactive(name)
3347 _setactive(name)
3348
3348
3349 def mqphasedefaults(repo, roots):
3349 def mqphasedefaults(repo, roots):
3350 """callback used to set mq changeset as secret when no phase data exists"""
3350 """callback used to set mq changeset as secret when no phase data exists"""
3351 if repo.mq.applied:
3351 if repo.mq.applied:
3352 if repo.ui.configbool('mq', 'secret', False):
3352 if repo.ui.configbool('mq', 'secret', False):
3353 mqphase = phases.secret
3353 mqphase = phases.secret
3354 else:
3354 else:
3355 mqphase = phases.draft
3355 mqphase = phases.draft
3356 qbase = repo[repo.mq.applied[0].node]
3356 qbase = repo[repo.mq.applied[0].node]
3357 roots[mqphase].add(qbase.node())
3357 roots[mqphase].add(qbase.node())
3358 return roots
3358 return roots
3359
3359
3360 def reposetup(ui, repo):
3360 def reposetup(ui, repo):
3361 class mqrepo(repo.__class__):
3361 class mqrepo(repo.__class__):
3362 @util.propertycache
3362 @util.propertycache
3363 def mq(self):
3363 def mq(self):
3364 return queue(self.ui, self.path)
3364 return queue(self.ui, self.path)
3365
3365
3366 def abortifwdirpatched(self, errmsg, force=False):
3366 def abortifwdirpatched(self, errmsg, force=False):
3367 if self.mq.applied and not force:
3367 if self.mq.applied and not force:
3368 parents = self.dirstate.parents()
3368 parents = self.dirstate.parents()
3369 patches = [s.node for s in self.mq.applied]
3369 patches = [s.node for s in self.mq.applied]
3370 if parents[0] in patches or parents[1] in patches:
3370 if parents[0] in patches or parents[1] in patches:
3371 raise util.Abort(errmsg)
3371 raise util.Abort(errmsg)
3372
3372
3373 def commit(self, text="", user=None, date=None, match=None,
3373 def commit(self, text="", user=None, date=None, match=None,
3374 force=False, editor=False, extra={}):
3374 force=False, editor=False, extra={}):
3375 self.abortifwdirpatched(
3375 self.abortifwdirpatched(
3376 _('cannot commit over an applied mq patch'),
3376 _('cannot commit over an applied mq patch'),
3377 force)
3377 force)
3378
3378
3379 return super(mqrepo, self).commit(text, user, date, match, force,
3379 return super(mqrepo, self).commit(text, user, date, match, force,
3380 editor, extra)
3380 editor, extra)
3381
3381
3382 def checkpush(self, force, revs):
3382 def checkpush(self, force, revs):
3383 if self.mq.applied and not force:
3383 if self.mq.applied and not force:
3384 outapplied = [e.node for e in self.mq.applied]
3384 outapplied = [e.node for e in self.mq.applied]
3385 if revs:
3385 if revs:
3386 # Assume applied patches have no non-patch descendants and
3386 # Assume applied patches have no non-patch descendants and
3387 # are not on remote already. Filtering any changeset not
3387 # are not on remote already. Filtering any changeset not
3388 # pushed.
3388 # pushed.
3389 heads = set(revs)
3389 heads = set(revs)
3390 for node in reversed(outapplied):
3390 for node in reversed(outapplied):
3391 if node in heads:
3391 if node in heads:
3392 break
3392 break
3393 else:
3393 else:
3394 outapplied.pop()
3394 outapplied.pop()
3395 # looking for pushed and shared changeset
3395 # looking for pushed and shared changeset
3396 for node in outapplied:
3396 for node in outapplied:
3397 if repo[node].phase() < phases.secret:
3397 if repo[node].phase() < phases.secret:
3398 raise util.Abort(_('source has mq patches applied'))
3398 raise util.Abort(_('source has mq patches applied'))
3399 # no non-secret patches pushed
3399 # no non-secret patches pushed
3400 super(mqrepo, self).checkpush(force, revs)
3400 super(mqrepo, self).checkpush(force, revs)
3401
3401
3402 def _findtags(self):
3402 def _findtags(self):
3403 '''augment tags from base class with patch tags'''
3403 '''augment tags from base class with patch tags'''
3404 result = super(mqrepo, self)._findtags()
3404 result = super(mqrepo, self)._findtags()
3405
3405
3406 q = self.mq
3406 q = self.mq
3407 if not q.applied:
3407 if not q.applied:
3408 return result
3408 return result
3409
3409
3410 mqtags = [(patch.node, patch.name) for patch in q.applied]
3410 mqtags = [(patch.node, patch.name) for patch in q.applied]
3411
3411
3412 try:
3412 try:
3413 self.changelog.rev(mqtags[-1][0])
3413 self.changelog.rev(mqtags[-1][0])
3414 except error.LookupError:
3414 except error.LookupError:
3415 self.ui.warn(_('mq status file refers to unknown node %s\n')
3415 self.ui.warn(_('mq status file refers to unknown node %s\n')
3416 % short(mqtags[-1][0]))
3416 % short(mqtags[-1][0]))
3417 return result
3417 return result
3418
3418
3419 mqtags.append((mqtags[-1][0], 'qtip'))
3419 mqtags.append((mqtags[-1][0], 'qtip'))
3420 mqtags.append((mqtags[0][0], 'qbase'))
3420 mqtags.append((mqtags[0][0], 'qbase'))
3421 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3421 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3422 tags = result[0]
3422 tags = result[0]
3423 for patch in mqtags:
3423 for patch in mqtags:
3424 if patch[1] in tags:
3424 if patch[1] in tags:
3425 self.ui.warn(_('tag %s overrides mq patch of the same '
3425 self.ui.warn(_('tag %s overrides mq patch of the same '
3426 'name\n') % patch[1])
3426 'name\n') % patch[1])
3427 else:
3427 else:
3428 tags[patch[1]] = patch[0]
3428 tags[patch[1]] = patch[0]
3429
3429
3430 return result
3430 return result
3431
3431
3432 def _branchtags(self, partial, lrev):
3432 def _branchtags(self, partial, lrev):
3433 q = self.mq
3433 q = self.mq
3434 cl = self.changelog
3434 cl = self.changelog
3435 qbase = None
3435 qbase = None
3436 if not q.applied:
3436 if not q.applied:
3437 if getattr(self, '_committingpatch', False):
3437 if getattr(self, '_committingpatch', False):
3438 # Committing a new patch, must be tip
3438 # Committing a new patch, must be tip
3439 qbase = len(cl) - 1
3439 qbase = len(cl) - 1
3440 else:
3440 else:
3441 qbasenode = q.applied[0].node
3441 qbasenode = q.applied[0].node
3442 try:
3442 try:
3443 qbase = cl.rev(qbasenode)
3443 qbase = cl.rev(qbasenode)
3444 except error.LookupError:
3444 except error.LookupError:
3445 self.ui.warn(_('mq status file refers to unknown node %s\n')
3445 self.ui.warn(_('mq status file refers to unknown node %s\n')
3446 % short(qbasenode))
3446 % short(qbasenode))
3447 if qbase is None:
3447 if qbase is None:
3448 return super(mqrepo, self)._branchtags(partial, lrev)
3448 return super(mqrepo, self)._branchtags(partial, lrev)
3449
3449
3450 start = lrev + 1
3450 start = lrev + 1
3451 if start < qbase:
3451 if start < qbase:
3452 # update the cache (excluding the patches) and save it
3452 # update the cache (excluding the patches) and save it
3453 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
3453 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
3454 self._updatebranchcache(partial, ctxgen)
3454 self._updatebranchcache(partial, ctxgen)
3455 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
3455 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
3456 start = qbase
3456 start = qbase
3457 # if start = qbase, the cache is as updated as it should be.
3457 # if start = qbase, the cache is as updated as it should be.
3458 # if start > qbase, the cache includes (part of) the patches.
3458 # if start > qbase, the cache includes (part of) the patches.
3459 # we might as well use it, but we won't save it.
3459 # we might as well use it, but we won't save it.
3460
3460
3461 # update the cache up to the tip
3461 # update the cache up to the tip
3462 ctxgen = (self[r] for r in xrange(start, len(cl)))
3462 ctxgen = (self[r] for r in xrange(start, len(cl)))
3463 self._updatebranchcache(partial, ctxgen)
3463 self._updatebranchcache(partial, ctxgen)
3464
3464
3465 return partial
3465 return partial
3466
3466
3467 if repo.local():
3467 if repo.local():
3468 repo.__class__ = mqrepo
3468 repo.__class__ = mqrepo
3469
3469
3470 repo._phasedefaults.append(mqphasedefaults)
3470 repo._phasedefaults.append(mqphasedefaults)
3471
3471
3472 def mqimport(orig, ui, repo, *args, **kwargs):
3472 def mqimport(orig, ui, repo, *args, **kwargs):
3473 if (util.safehasattr(repo, 'abortifwdirpatched')
3473 if (util.safehasattr(repo, 'abortifwdirpatched')
3474 and not kwargs.get('no_commit', False)):
3474 and not kwargs.get('no_commit', False)):
3475 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3475 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3476 kwargs.get('force'))
3476 kwargs.get('force'))
3477 return orig(ui, repo, *args, **kwargs)
3477 return orig(ui, repo, *args, **kwargs)
3478
3478
3479 def mqinit(orig, ui, *args, **kwargs):
3479 def mqinit(orig, ui, *args, **kwargs):
3480 mq = kwargs.pop('mq', None)
3480 mq = kwargs.pop('mq', None)
3481
3481
3482 if not mq:
3482 if not mq:
3483 return orig(ui, *args, **kwargs)
3483 return orig(ui, *args, **kwargs)
3484
3484
3485 if args:
3485 if args:
3486 repopath = args[0]
3486 repopath = args[0]
3487 if not hg.islocal(repopath):
3487 if not hg.islocal(repopath):
3488 raise util.Abort(_('only a local queue repository '
3488 raise util.Abort(_('only a local queue repository '
3489 'may be initialized'))
3489 'may be initialized'))
3490 else:
3490 else:
3491 repopath = cmdutil.findrepo(os.getcwd())
3491 repopath = cmdutil.findrepo(os.getcwd())
3492 if not repopath:
3492 if not repopath:
3493 raise util.Abort(_('there is no Mercurial repository here '
3493 raise util.Abort(_('there is no Mercurial repository here '
3494 '(.hg not found)'))
3494 '(.hg not found)'))
3495 repo = hg.repository(ui, repopath)
3495 repo = hg.repository(ui, repopath)
3496 return qinit(ui, repo, True)
3496 return qinit(ui, repo, True)
3497
3497
3498 def mqcommand(orig, ui, repo, *args, **kwargs):
3498 def mqcommand(orig, ui, repo, *args, **kwargs):
3499 """Add --mq option to operate on patch repository instead of main"""
3499 """Add --mq option to operate on patch repository instead of main"""
3500
3500
3501 # some commands do not like getting unknown options
3501 # some commands do not like getting unknown options
3502 mq = kwargs.pop('mq', None)
3502 mq = kwargs.pop('mq', None)
3503
3503
3504 if not mq:
3504 if not mq:
3505 return orig(ui, repo, *args, **kwargs)
3505 return orig(ui, repo, *args, **kwargs)
3506
3506
3507 q = repo.mq
3507 q = repo.mq
3508 r = q.qrepo()
3508 r = q.qrepo()
3509 if not r:
3509 if not r:
3510 raise util.Abort(_('no queue repository'))
3510 raise util.Abort(_('no queue repository'))
3511 return orig(r.ui, r, *args, **kwargs)
3511 return orig(r.ui, r, *args, **kwargs)
3512
3512
3513 def summary(orig, ui, repo, *args, **kwargs):
3513 def summary(orig, ui, repo, *args, **kwargs):
3514 r = orig(ui, repo, *args, **kwargs)
3514 r = orig(ui, repo, *args, **kwargs)
3515 q = repo.mq
3515 q = repo.mq
3516 m = []
3516 m = []
3517 a, u = len(q.applied), len(q.unapplied(repo))
3517 a, u = len(q.applied), len(q.unapplied(repo))
3518 if a:
3518 if a:
3519 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3519 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3520 if u:
3520 if u:
3521 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3521 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3522 if m:
3522 if m:
3523 ui.write("mq: %s\n" % ', '.join(m))
3523 ui.write("mq: %s\n" % ', '.join(m))
3524 else:
3524 else:
3525 ui.note(_("mq: (empty queue)\n"))
3525 ui.note(_("mq: (empty queue)\n"))
3526 return r
3526 return r
3527
3527
3528 def revsetmq(repo, subset, x):
3528 def revsetmq(repo, subset, x):
3529 """``mq()``
3529 """``mq()``
3530 Changesets managed by MQ.
3530 Changesets managed by MQ.
3531 """
3531 """
3532 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3532 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3533 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3533 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3534 return [r for r in subset if r in applied]
3534 return [r for r in subset if r in applied]
3535
3535
3536 def extsetup(ui):
3536 def extsetup(ui):
3537 revset.symbols['mq'] = revsetmq
3537 revset.symbols['mq'] = revsetmq
3538
3538
3539 # tell hggettext to extract docstrings from these functions:
3539 # tell hggettext to extract docstrings from these functions:
3540 i18nfunctions = [revsetmq]
3540 i18nfunctions = [revsetmq]
3541
3541
3542 def uisetup(ui):
3542 def uisetup(ui):
3543 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3543 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3544
3544
3545 extensions.wrapcommand(commands.table, 'import', mqimport)
3545 extensions.wrapcommand(commands.table, 'import', mqimport)
3546 extensions.wrapcommand(commands.table, 'summary', summary)
3546 extensions.wrapcommand(commands.table, 'summary', summary)
3547
3547
3548 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3548 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3549 entry[1].extend(mqopt)
3549 entry[1].extend(mqopt)
3550
3550
3551 nowrap = set(commands.norepo.split(" "))
3551 nowrap = set(commands.norepo.split(" "))
3552
3552
3553 def dotable(cmdtable):
3553 def dotable(cmdtable):
3554 for cmd in cmdtable.keys():
3554 for cmd in cmdtable.keys():
3555 cmd = cmdutil.parsealiases(cmd)[0]
3555 cmd = cmdutil.parsealiases(cmd)[0]
3556 if cmd in nowrap:
3556 if cmd in nowrap:
3557 continue
3557 continue
3558 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3558 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3559 entry[1].extend(mqopt)
3559 entry[1].extend(mqopt)
3560
3560
3561 dotable(commands.table)
3561 dotable(commands.table)
3562
3562
3563 for extname, extmodule in extensions.extensions():
3563 for extname, extmodule in extensions.extensions():
3564 if extmodule.__file__ != __file__:
3564 if extmodule.__file__ != __file__:
3565 dotable(getattr(extmodule, 'cmdtable', {}))
3565 dotable(getattr(extmodule, 'cmdtable', {}))
3566
3566
3567
3567
3568 colortable = {'qguard.negative': 'red',
3568 colortable = {'qguard.negative': 'red',
3569 'qguard.positive': 'yellow',
3569 'qguard.positive': 'yellow',
3570 'qguard.unguarded': 'green',
3570 'qguard.unguarded': 'green',
3571 'qseries.applied': 'blue bold underline',
3571 'qseries.applied': 'blue bold underline',
3572 'qseries.guarded': 'black bold',
3572 'qseries.guarded': 'black bold',
3573 'qseries.missing': 'red bold',
3573 'qseries.missing': 'red bold',
3574 'qseries.unapplied': 'black bold'}
3574 'qseries.unapplied': 'black bold'}
General Comments 0
You need to be logged in to leave comments. Login now