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