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