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