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