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