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