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