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