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