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