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