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