##// END OF EJS Templates
qrefresh: allow to specify '--message/'--logfile' and '--edit' at the same time...
FUJIWARA Katsunori -
r21713:7a51bced default
parent child Browse files
Show More
@@ -1,3467 +1,3464 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 edit = opts.get('edit')
1029 edit = opts.get('edit')
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 editor = cmdutil.getcommiteditor()
1084 editor = cmdutil.getcommiteditor()
1085 if edit:
1085 if edit:
1086 def finishdesc(desc):
1086 def finishdesc(desc):
1087 if desc.rstrip():
1087 if desc.rstrip():
1088 return desc
1088 return desc
1089 else:
1089 else:
1090 return defaultmsg
1090 return defaultmsg
1091 # i18n: this message is shown in editor with "HG: " prefix
1091 # i18n: this message is shown in editor with "HG: " prefix
1092 extramsg = _('Leave message empty to use default message.')
1092 extramsg = _('Leave message empty to use default message.')
1093 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1093 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1094 extramsg=extramsg)
1094 extramsg=extramsg)
1095 commitmsg = msg
1095 commitmsg = msg
1096 else:
1096 else:
1097 commitmsg = msg or defaultmsg
1097 commitmsg = msg or defaultmsg
1098
1098
1099 n = newcommit(repo, None, commitmsg, user, date, match=match,
1099 n = newcommit(repo, None, commitmsg, user, date, match=match,
1100 force=True, editor=editor)
1100 force=True, editor=editor)
1101 if n is None:
1101 if n is None:
1102 raise util.Abort(_("repo commit failed"))
1102 raise util.Abort(_("repo commit failed"))
1103 try:
1103 try:
1104 self.fullseries[insert:insert] = [patchfn]
1104 self.fullseries[insert:insert] = [patchfn]
1105 self.applied.append(statusentry(n, patchfn))
1105 self.applied.append(statusentry(n, patchfn))
1106 self.parseseries()
1106 self.parseseries()
1107 self.seriesdirty = True
1107 self.seriesdirty = True
1108 self.applieddirty = True
1108 self.applieddirty = True
1109 nctx = repo[n]
1109 nctx = repo[n]
1110 if nctx.description() != defaultmsg.rstrip():
1110 if nctx.description() != defaultmsg.rstrip():
1111 msg = nctx.description() + "\n\n"
1111 msg = nctx.description() + "\n\n"
1112 p.write(msg)
1112 p.write(msg)
1113 if commitfiles:
1113 if commitfiles:
1114 parent = self.qparents(repo, n)
1114 parent = self.qparents(repo, n)
1115 if inclsubs:
1115 if inclsubs:
1116 self.putsubstate2changes(substatestate, changes)
1116 self.putsubstate2changes(substatestate, changes)
1117 chunks = patchmod.diff(repo, node1=parent, node2=n,
1117 chunks = patchmod.diff(repo, node1=parent, node2=n,
1118 changes=changes, opts=diffopts)
1118 changes=changes, opts=diffopts)
1119 for chunk in chunks:
1119 for chunk in chunks:
1120 p.write(chunk)
1120 p.write(chunk)
1121 p.close()
1121 p.close()
1122 r = self.qrepo()
1122 r = self.qrepo()
1123 if r:
1123 if r:
1124 r[None].add([patchfn])
1124 r[None].add([patchfn])
1125 except: # re-raises
1125 except: # re-raises
1126 repo.rollback()
1126 repo.rollback()
1127 raise
1127 raise
1128 except Exception:
1128 except Exception:
1129 patchpath = self.join(patchfn)
1129 patchpath = self.join(patchfn)
1130 try:
1130 try:
1131 os.unlink(patchpath)
1131 os.unlink(patchpath)
1132 except OSError:
1132 except OSError:
1133 self.ui.warn(_('error unlinking %s\n') % patchpath)
1133 self.ui.warn(_('error unlinking %s\n') % patchpath)
1134 raise
1134 raise
1135 self.removeundo(repo)
1135 self.removeundo(repo)
1136 finally:
1136 finally:
1137 release(wlock)
1137 release(wlock)
1138
1138
1139 def isapplied(self, patch):
1139 def isapplied(self, patch):
1140 """returns (index, rev, patch)"""
1140 """returns (index, rev, patch)"""
1141 for i, a in enumerate(self.applied):
1141 for i, a in enumerate(self.applied):
1142 if a.name == patch:
1142 if a.name == patch:
1143 return (i, a.node, a.name)
1143 return (i, a.node, a.name)
1144 return None
1144 return None
1145
1145
1146 # if the exact patch name does not exist, we try a few
1146 # if the exact patch name does not exist, we try a few
1147 # variations. If strict is passed, we try only #1
1147 # variations. If strict is passed, we try only #1
1148 #
1148 #
1149 # 1) a number (as string) to indicate an offset in the series file
1149 # 1) a number (as string) to indicate an offset in the series file
1150 # 2) a unique substring of the patch name was given
1150 # 2) a unique substring of the patch name was given
1151 # 3) patchname[-+]num to indicate an offset in the series file
1151 # 3) patchname[-+]num to indicate an offset in the series file
1152 def lookup(self, patch, strict=False):
1152 def lookup(self, patch, strict=False):
1153 def partialname(s):
1153 def partialname(s):
1154 if s in self.series:
1154 if s in self.series:
1155 return s
1155 return s
1156 matches = [x for x in self.series if s in x]
1156 matches = [x for x in self.series if s in x]
1157 if len(matches) > 1:
1157 if len(matches) > 1:
1158 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1158 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1159 for m in matches:
1159 for m in matches:
1160 self.ui.warn(' %s\n' % m)
1160 self.ui.warn(' %s\n' % m)
1161 return None
1161 return None
1162 if matches:
1162 if matches:
1163 return matches[0]
1163 return matches[0]
1164 if self.series and self.applied:
1164 if self.series and self.applied:
1165 if s == 'qtip':
1165 if s == 'qtip':
1166 return self.series[self.seriesend(True) - 1]
1166 return self.series[self.seriesend(True) - 1]
1167 if s == 'qbase':
1167 if s == 'qbase':
1168 return self.series[0]
1168 return self.series[0]
1169 return None
1169 return None
1170
1170
1171 if patch in self.series:
1171 if patch in self.series:
1172 return patch
1172 return patch
1173
1173
1174 if not os.path.isfile(self.join(patch)):
1174 if not os.path.isfile(self.join(patch)):
1175 try:
1175 try:
1176 sno = int(patch)
1176 sno = int(patch)
1177 except (ValueError, OverflowError):
1177 except (ValueError, OverflowError):
1178 pass
1178 pass
1179 else:
1179 else:
1180 if -len(self.series) <= sno < len(self.series):
1180 if -len(self.series) <= sno < len(self.series):
1181 return self.series[sno]
1181 return self.series[sno]
1182
1182
1183 if not strict:
1183 if not strict:
1184 res = partialname(patch)
1184 res = partialname(patch)
1185 if res:
1185 if res:
1186 return res
1186 return res
1187 minus = patch.rfind('-')
1187 minus = patch.rfind('-')
1188 if minus >= 0:
1188 if minus >= 0:
1189 res = partialname(patch[:minus])
1189 res = partialname(patch[:minus])
1190 if res:
1190 if res:
1191 i = self.series.index(res)
1191 i = self.series.index(res)
1192 try:
1192 try:
1193 off = int(patch[minus + 1:] or 1)
1193 off = int(patch[minus + 1:] or 1)
1194 except (ValueError, OverflowError):
1194 except (ValueError, OverflowError):
1195 pass
1195 pass
1196 else:
1196 else:
1197 if i - off >= 0:
1197 if i - off >= 0:
1198 return self.series[i - off]
1198 return self.series[i - off]
1199 plus = patch.rfind('+')
1199 plus = patch.rfind('+')
1200 if plus >= 0:
1200 if plus >= 0:
1201 res = partialname(patch[:plus])
1201 res = partialname(patch[:plus])
1202 if res:
1202 if res:
1203 i = self.series.index(res)
1203 i = self.series.index(res)
1204 try:
1204 try:
1205 off = int(patch[plus + 1:] or 1)
1205 off = int(patch[plus + 1:] or 1)
1206 except (ValueError, OverflowError):
1206 except (ValueError, OverflowError):
1207 pass
1207 pass
1208 else:
1208 else:
1209 if i + off < len(self.series):
1209 if i + off < len(self.series):
1210 return self.series[i + off]
1210 return self.series[i + off]
1211 raise util.Abort(_("patch %s not in series") % patch)
1211 raise util.Abort(_("patch %s not in series") % patch)
1212
1212
1213 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1213 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1214 all=False, move=False, exact=False, nobackup=False,
1214 all=False, move=False, exact=False, nobackup=False,
1215 keepchanges=False):
1215 keepchanges=False):
1216 self.checkkeepchanges(keepchanges, force)
1216 self.checkkeepchanges(keepchanges, force)
1217 diffopts = self.diffopts()
1217 diffopts = self.diffopts()
1218 wlock = repo.wlock()
1218 wlock = repo.wlock()
1219 try:
1219 try:
1220 heads = []
1220 heads = []
1221 for hs in repo.branchmap().itervalues():
1221 for hs in repo.branchmap().itervalues():
1222 heads.extend(hs)
1222 heads.extend(hs)
1223 if not heads:
1223 if not heads:
1224 heads = [nullid]
1224 heads = [nullid]
1225 if repo.dirstate.p1() not in heads and not exact:
1225 if repo.dirstate.p1() not in heads and not exact:
1226 self.ui.status(_("(working directory not at a head)\n"))
1226 self.ui.status(_("(working directory not at a head)\n"))
1227
1227
1228 if not self.series:
1228 if not self.series:
1229 self.ui.warn(_('no patches in series\n'))
1229 self.ui.warn(_('no patches in series\n'))
1230 return 0
1230 return 0
1231
1231
1232 # Suppose our series file is: A B C and the current 'top'
1232 # Suppose our series file is: A B C and the current 'top'
1233 # patch is B. qpush C should be performed (moving forward)
1233 # patch is B. qpush C should be performed (moving forward)
1234 # qpush B is a NOP (no change) qpush A is an error (can't
1234 # qpush B is a NOP (no change) qpush A is an error (can't
1235 # go backwards with qpush)
1235 # go backwards with qpush)
1236 if patch:
1236 if patch:
1237 patch = self.lookup(patch)
1237 patch = self.lookup(patch)
1238 info = self.isapplied(patch)
1238 info = self.isapplied(patch)
1239 if info and info[0] >= len(self.applied) - 1:
1239 if info and info[0] >= len(self.applied) - 1:
1240 self.ui.warn(
1240 self.ui.warn(
1241 _('qpush: %s is already at the top\n') % patch)
1241 _('qpush: %s is already at the top\n') % patch)
1242 return 0
1242 return 0
1243
1243
1244 pushable, reason = self.pushable(patch)
1244 pushable, reason = self.pushable(patch)
1245 if pushable:
1245 if pushable:
1246 if self.series.index(patch) < self.seriesend():
1246 if self.series.index(patch) < self.seriesend():
1247 raise util.Abort(
1247 raise util.Abort(
1248 _("cannot push to a previous patch: %s") % patch)
1248 _("cannot push to a previous patch: %s") % patch)
1249 else:
1249 else:
1250 if reason:
1250 if reason:
1251 reason = _('guarded by %s') % reason
1251 reason = _('guarded by %s') % reason
1252 else:
1252 else:
1253 reason = _('no matching guards')
1253 reason = _('no matching guards')
1254 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1254 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1255 return 1
1255 return 1
1256 elif all:
1256 elif all:
1257 patch = self.series[-1]
1257 patch = self.series[-1]
1258 if self.isapplied(patch):
1258 if self.isapplied(patch):
1259 self.ui.warn(_('all patches are currently applied\n'))
1259 self.ui.warn(_('all patches are currently applied\n'))
1260 return 0
1260 return 0
1261
1261
1262 # Following the above example, starting at 'top' of B:
1262 # Following the above example, starting at 'top' of B:
1263 # qpush should be performed (pushes C), but a subsequent
1263 # qpush should be performed (pushes C), but a subsequent
1264 # qpush without an argument is an error (nothing to
1264 # qpush without an argument is an error (nothing to
1265 # apply). This allows a loop of "...while hg qpush..." to
1265 # apply). This allows a loop of "...while hg qpush..." to
1266 # work as it detects an error when done
1266 # work as it detects an error when done
1267 start = self.seriesend()
1267 start = self.seriesend()
1268 if start == len(self.series):
1268 if start == len(self.series):
1269 self.ui.warn(_('patch series already fully applied\n'))
1269 self.ui.warn(_('patch series already fully applied\n'))
1270 return 1
1270 return 1
1271 if not force and not keepchanges:
1271 if not force and not keepchanges:
1272 self.checklocalchanges(repo, refresh=self.applied)
1272 self.checklocalchanges(repo, refresh=self.applied)
1273
1273
1274 if exact:
1274 if exact:
1275 if keepchanges:
1275 if keepchanges:
1276 raise util.Abort(
1276 raise util.Abort(
1277 _("cannot use --exact and --keep-changes together"))
1277 _("cannot use --exact and --keep-changes together"))
1278 if move:
1278 if move:
1279 raise util.Abort(_('cannot use --exact and --move '
1279 raise util.Abort(_('cannot use --exact and --move '
1280 'together'))
1280 'together'))
1281 if self.applied:
1281 if self.applied:
1282 raise util.Abort(_('cannot push --exact with applied '
1282 raise util.Abort(_('cannot push --exact with applied '
1283 'patches'))
1283 'patches'))
1284 root = self.series[start]
1284 root = self.series[start]
1285 target = patchheader(self.join(root), self.plainmode).parent
1285 target = patchheader(self.join(root), self.plainmode).parent
1286 if not target:
1286 if not target:
1287 raise util.Abort(
1287 raise util.Abort(
1288 _("%s does not have a parent recorded") % root)
1288 _("%s does not have a parent recorded") % root)
1289 if not repo[target] == repo['.']:
1289 if not repo[target] == repo['.']:
1290 hg.update(repo, target)
1290 hg.update(repo, target)
1291
1291
1292 if move:
1292 if move:
1293 if not patch:
1293 if not patch:
1294 raise util.Abort(_("please specify the patch to move"))
1294 raise util.Abort(_("please specify the patch to move"))
1295 for fullstart, rpn in enumerate(self.fullseries):
1295 for fullstart, rpn in enumerate(self.fullseries):
1296 # strip markers for patch guards
1296 # strip markers for patch guards
1297 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1297 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1298 break
1298 break
1299 for i, rpn in enumerate(self.fullseries[fullstart:]):
1299 for i, rpn in enumerate(self.fullseries[fullstart:]):
1300 # strip markers for patch guards
1300 # strip markers for patch guards
1301 if self.guard_re.split(rpn, 1)[0] == patch:
1301 if self.guard_re.split(rpn, 1)[0] == patch:
1302 break
1302 break
1303 index = fullstart + i
1303 index = fullstart + i
1304 assert index < len(self.fullseries)
1304 assert index < len(self.fullseries)
1305 fullpatch = self.fullseries[index]
1305 fullpatch = self.fullseries[index]
1306 del self.fullseries[index]
1306 del self.fullseries[index]
1307 self.fullseries.insert(fullstart, fullpatch)
1307 self.fullseries.insert(fullstart, fullpatch)
1308 self.parseseries()
1308 self.parseseries()
1309 self.seriesdirty = True
1309 self.seriesdirty = True
1310
1310
1311 self.applieddirty = True
1311 self.applieddirty = True
1312 if start > 0:
1312 if start > 0:
1313 self.checktoppatch(repo)
1313 self.checktoppatch(repo)
1314 if not patch:
1314 if not patch:
1315 patch = self.series[start]
1315 patch = self.series[start]
1316 end = start + 1
1316 end = start + 1
1317 else:
1317 else:
1318 end = self.series.index(patch, start) + 1
1318 end = self.series.index(patch, start) + 1
1319
1319
1320 tobackup = set()
1320 tobackup = set()
1321 if (not nobackup and force) or keepchanges:
1321 if (not nobackup and force) or keepchanges:
1322 m, a, r, d = self.checklocalchanges(repo, force=True)
1322 m, a, r, d = self.checklocalchanges(repo, force=True)
1323 if keepchanges:
1323 if keepchanges:
1324 tobackup.update(m + a + r + d)
1324 tobackup.update(m + a + r + d)
1325 else:
1325 else:
1326 tobackup.update(m + a)
1326 tobackup.update(m + a)
1327
1327
1328 s = self.series[start:end]
1328 s = self.series[start:end]
1329 all_files = set()
1329 all_files = set()
1330 try:
1330 try:
1331 if mergeq:
1331 if mergeq:
1332 ret = self.mergepatch(repo, mergeq, s, diffopts)
1332 ret = self.mergepatch(repo, mergeq, s, diffopts)
1333 else:
1333 else:
1334 ret = self.apply(repo, s, list, all_files=all_files,
1334 ret = self.apply(repo, s, list, all_files=all_files,
1335 tobackup=tobackup, keepchanges=keepchanges)
1335 tobackup=tobackup, keepchanges=keepchanges)
1336 except: # re-raises
1336 except: # re-raises
1337 self.ui.warn(_('cleaning up working directory...'))
1337 self.ui.warn(_('cleaning up working directory...'))
1338 node = repo.dirstate.p1()
1338 node = repo.dirstate.p1()
1339 hg.revert(repo, node, None)
1339 hg.revert(repo, node, None)
1340 # only remove unknown files that we know we touched or
1340 # only remove unknown files that we know we touched or
1341 # created while patching
1341 # created while patching
1342 for f in all_files:
1342 for f in all_files:
1343 if f not in repo.dirstate:
1343 if f not in repo.dirstate:
1344 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1344 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1345 self.ui.warn(_('done\n'))
1345 self.ui.warn(_('done\n'))
1346 raise
1346 raise
1347
1347
1348 if not self.applied:
1348 if not self.applied:
1349 return ret[0]
1349 return ret[0]
1350 top = self.applied[-1].name
1350 top = self.applied[-1].name
1351 if ret[0] and ret[0] > 1:
1351 if ret[0] and ret[0] > 1:
1352 msg = _("errors during apply, please fix and refresh %s\n")
1352 msg = _("errors during apply, please fix and refresh %s\n")
1353 self.ui.write(msg % top)
1353 self.ui.write(msg % top)
1354 else:
1354 else:
1355 self.ui.write(_("now at: %s\n") % top)
1355 self.ui.write(_("now at: %s\n") % top)
1356 return ret[0]
1356 return ret[0]
1357
1357
1358 finally:
1358 finally:
1359 wlock.release()
1359 wlock.release()
1360
1360
1361 def pop(self, repo, patch=None, force=False, update=True, all=False,
1361 def pop(self, repo, patch=None, force=False, update=True, all=False,
1362 nobackup=False, keepchanges=False):
1362 nobackup=False, keepchanges=False):
1363 self.checkkeepchanges(keepchanges, force)
1363 self.checkkeepchanges(keepchanges, force)
1364 wlock = repo.wlock()
1364 wlock = repo.wlock()
1365 try:
1365 try:
1366 if patch:
1366 if patch:
1367 # index, rev, patch
1367 # index, rev, patch
1368 info = self.isapplied(patch)
1368 info = self.isapplied(patch)
1369 if not info:
1369 if not info:
1370 patch = self.lookup(patch)
1370 patch = self.lookup(patch)
1371 info = self.isapplied(patch)
1371 info = self.isapplied(patch)
1372 if not info:
1372 if not info:
1373 raise util.Abort(_("patch %s is not applied") % patch)
1373 raise util.Abort(_("patch %s is not applied") % patch)
1374
1374
1375 if not self.applied:
1375 if not self.applied:
1376 # Allow qpop -a to work repeatedly,
1376 # Allow qpop -a to work repeatedly,
1377 # but not qpop without an argument
1377 # but not qpop without an argument
1378 self.ui.warn(_("no patches applied\n"))
1378 self.ui.warn(_("no patches applied\n"))
1379 return not all
1379 return not all
1380
1380
1381 if all:
1381 if all:
1382 start = 0
1382 start = 0
1383 elif patch:
1383 elif patch:
1384 start = info[0] + 1
1384 start = info[0] + 1
1385 else:
1385 else:
1386 start = len(self.applied) - 1
1386 start = len(self.applied) - 1
1387
1387
1388 if start >= len(self.applied):
1388 if start >= len(self.applied):
1389 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1389 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1390 return
1390 return
1391
1391
1392 if not update:
1392 if not update:
1393 parents = repo.dirstate.parents()
1393 parents = repo.dirstate.parents()
1394 rr = [x.node for x in self.applied]
1394 rr = [x.node for x in self.applied]
1395 for p in parents:
1395 for p in parents:
1396 if p in rr:
1396 if p in rr:
1397 self.ui.warn(_("qpop: forcing dirstate update\n"))
1397 self.ui.warn(_("qpop: forcing dirstate update\n"))
1398 update = True
1398 update = True
1399 else:
1399 else:
1400 parents = [p.node() for p in repo[None].parents()]
1400 parents = [p.node() for p in repo[None].parents()]
1401 needupdate = False
1401 needupdate = False
1402 for entry in self.applied[start:]:
1402 for entry in self.applied[start:]:
1403 if entry.node in parents:
1403 if entry.node in parents:
1404 needupdate = True
1404 needupdate = True
1405 break
1405 break
1406 update = needupdate
1406 update = needupdate
1407
1407
1408 tobackup = set()
1408 tobackup = set()
1409 if update:
1409 if update:
1410 m, a, r, d = self.checklocalchanges(
1410 m, a, r, d = self.checklocalchanges(
1411 repo, force=force or keepchanges)
1411 repo, force=force or keepchanges)
1412 if force:
1412 if force:
1413 if not nobackup:
1413 if not nobackup:
1414 tobackup.update(m + a)
1414 tobackup.update(m + a)
1415 elif keepchanges:
1415 elif keepchanges:
1416 tobackup.update(m + a + r + d)
1416 tobackup.update(m + a + r + d)
1417
1417
1418 self.applieddirty = True
1418 self.applieddirty = True
1419 end = len(self.applied)
1419 end = len(self.applied)
1420 rev = self.applied[start].node
1420 rev = self.applied[start].node
1421
1421
1422 try:
1422 try:
1423 heads = repo.changelog.heads(rev)
1423 heads = repo.changelog.heads(rev)
1424 except error.LookupError:
1424 except error.LookupError:
1425 node = short(rev)
1425 node = short(rev)
1426 raise util.Abort(_('trying to pop unknown node %s') % node)
1426 raise util.Abort(_('trying to pop unknown node %s') % node)
1427
1427
1428 if heads != [self.applied[-1].node]:
1428 if heads != [self.applied[-1].node]:
1429 raise util.Abort(_("popping would remove a revision not "
1429 raise util.Abort(_("popping would remove a revision not "
1430 "managed by this patch queue"))
1430 "managed by this patch queue"))
1431 if not repo[self.applied[-1].node].mutable():
1431 if not repo[self.applied[-1].node].mutable():
1432 raise util.Abort(
1432 raise util.Abort(
1433 _("popping would remove an immutable revision"),
1433 _("popping would remove an immutable revision"),
1434 hint=_('see "hg help phases" for details'))
1434 hint=_('see "hg help phases" for details'))
1435
1435
1436 # we know there are no local changes, so we can make a simplified
1436 # we know there are no local changes, so we can make a simplified
1437 # form of hg.update.
1437 # form of hg.update.
1438 if update:
1438 if update:
1439 qp = self.qparents(repo, rev)
1439 qp = self.qparents(repo, rev)
1440 ctx = repo[qp]
1440 ctx = repo[qp]
1441 m, a, r, d = repo.status(qp, '.')[:4]
1441 m, a, r, d = repo.status(qp, '.')[:4]
1442 if d:
1442 if d:
1443 raise util.Abort(_("deletions found between repo revs"))
1443 raise util.Abort(_("deletions found between repo revs"))
1444
1444
1445 tobackup = set(a + m + r) & tobackup
1445 tobackup = set(a + m + r) & tobackup
1446 if keepchanges and tobackup:
1446 if keepchanges and tobackup:
1447 raise util.Abort(_("local changes found, refresh first"))
1447 raise util.Abort(_("local changes found, refresh first"))
1448 self.backup(repo, tobackup)
1448 self.backup(repo, tobackup)
1449
1449
1450 for f in a:
1450 for f in a:
1451 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1451 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1452 repo.dirstate.drop(f)
1452 repo.dirstate.drop(f)
1453 for f in m + r:
1453 for f in m + r:
1454 fctx = ctx[f]
1454 fctx = ctx[f]
1455 repo.wwrite(f, fctx.data(), fctx.flags())
1455 repo.wwrite(f, fctx.data(), fctx.flags())
1456 repo.dirstate.normal(f)
1456 repo.dirstate.normal(f)
1457 repo.setparents(qp, nullid)
1457 repo.setparents(qp, nullid)
1458 for patch in reversed(self.applied[start:end]):
1458 for patch in reversed(self.applied[start:end]):
1459 self.ui.status(_("popping %s\n") % patch.name)
1459 self.ui.status(_("popping %s\n") % patch.name)
1460 del self.applied[start:end]
1460 del self.applied[start:end]
1461 strip(self.ui, repo, [rev], update=False, backup='strip')
1461 strip(self.ui, repo, [rev], update=False, backup='strip')
1462 for s, state in repo['.'].substate.items():
1462 for s, state in repo['.'].substate.items():
1463 repo['.'].sub(s).get(state)
1463 repo['.'].sub(s).get(state)
1464 if self.applied:
1464 if self.applied:
1465 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1465 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1466 else:
1466 else:
1467 self.ui.write(_("patch queue now empty\n"))
1467 self.ui.write(_("patch queue now empty\n"))
1468 finally:
1468 finally:
1469 wlock.release()
1469 wlock.release()
1470
1470
1471 def diff(self, repo, pats, opts):
1471 def diff(self, repo, pats, opts):
1472 top, patch = self.checktoppatch(repo)
1472 top, patch = self.checktoppatch(repo)
1473 if not top:
1473 if not top:
1474 self.ui.write(_("no patches applied\n"))
1474 self.ui.write(_("no patches applied\n"))
1475 return
1475 return
1476 qp = self.qparents(repo, top)
1476 qp = self.qparents(repo, top)
1477 if opts.get('reverse'):
1477 if opts.get('reverse'):
1478 node1, node2 = None, qp
1478 node1, node2 = None, qp
1479 else:
1479 else:
1480 node1, node2 = qp, None
1480 node1, node2 = qp, None
1481 diffopts = self.diffopts(opts, patch)
1481 diffopts = self.diffopts(opts, patch)
1482 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1482 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1483
1483
1484 def refresh(self, repo, pats=None, **opts):
1484 def refresh(self, repo, pats=None, **opts):
1485 if not self.applied:
1485 if not self.applied:
1486 self.ui.write(_("no patches applied\n"))
1486 self.ui.write(_("no patches applied\n"))
1487 return 1
1487 return 1
1488 msg = opts.get('msg', '').rstrip()
1488 msg = opts.get('msg', '').rstrip()
1489 edit = opts.get('edit')
1489 edit = opts.get('edit')
1490 newuser = opts.get('user')
1490 newuser = opts.get('user')
1491 newdate = opts.get('date')
1491 newdate = opts.get('date')
1492 if newdate:
1492 if newdate:
1493 newdate = '%d %d' % util.parsedate(newdate)
1493 newdate = '%d %d' % util.parsedate(newdate)
1494 wlock = repo.wlock()
1494 wlock = repo.wlock()
1495
1495
1496 try:
1496 try:
1497 self.checktoppatch(repo)
1497 self.checktoppatch(repo)
1498 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1498 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1499 if repo.changelog.heads(top) != [top]:
1499 if repo.changelog.heads(top) != [top]:
1500 raise util.Abort(_("cannot refresh a revision with children"))
1500 raise util.Abort(_("cannot refresh a revision with children"))
1501 if not repo[top].mutable():
1501 if not repo[top].mutable():
1502 raise util.Abort(_("cannot refresh immutable revision"),
1502 raise util.Abort(_("cannot refresh immutable revision"),
1503 hint=_('see "hg help phases" for details'))
1503 hint=_('see "hg help phases" for details'))
1504
1504
1505 cparents = repo.changelog.parents(top)
1505 cparents = repo.changelog.parents(top)
1506 patchparent = self.qparents(repo, top)
1506 patchparent = self.qparents(repo, top)
1507
1507
1508 inclsubs = checksubstate(repo, hex(patchparent))
1508 inclsubs = checksubstate(repo, hex(patchparent))
1509 if inclsubs:
1509 if inclsubs:
1510 substatestate = repo.dirstate['.hgsubstate']
1510 substatestate = repo.dirstate['.hgsubstate']
1511
1511
1512 ph = patchheader(self.join(patchfn), self.plainmode)
1512 ph = patchheader(self.join(patchfn), self.plainmode)
1513 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1513 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1514 if newuser:
1514 if newuser:
1515 ph.setuser(newuser)
1515 ph.setuser(newuser)
1516 if newdate:
1516 if newdate:
1517 ph.setdate(newdate)
1517 ph.setdate(newdate)
1518 ph.setparent(hex(patchparent))
1518 ph.setparent(hex(patchparent))
1519
1519
1520 # only commit new patch when write is complete
1520 # only commit new patch when write is complete
1521 patchf = self.opener(patchfn, 'w', atomictemp=True)
1521 patchf = self.opener(patchfn, 'w', atomictemp=True)
1522
1522
1523 # update the dirstate in place, strip off the qtip commit
1523 # update the dirstate in place, strip off the qtip commit
1524 # and then commit.
1524 # and then commit.
1525 #
1525 #
1526 # this should really read:
1526 # this should really read:
1527 # mm, dd, aa = repo.status(top, patchparent)[:3]
1527 # mm, dd, aa = repo.status(top, patchparent)[:3]
1528 # but we do it backwards to take advantage of manifest/changelog
1528 # but we do it backwards to take advantage of manifest/changelog
1529 # caching against the next repo.status call
1529 # caching against the next repo.status call
1530 mm, aa, dd = repo.status(patchparent, top)[:3]
1530 mm, aa, dd = repo.status(patchparent, top)[:3]
1531 changes = repo.changelog.read(top)
1531 changes = repo.changelog.read(top)
1532 man = repo.manifest.read(changes[0])
1532 man = repo.manifest.read(changes[0])
1533 aaa = aa[:]
1533 aaa = aa[:]
1534 matchfn = scmutil.match(repo[None], pats, opts)
1534 matchfn = scmutil.match(repo[None], pats, opts)
1535 # in short mode, we only diff the files included in the
1535 # in short mode, we only diff the files included in the
1536 # patch already plus specified files
1536 # patch already plus specified files
1537 if opts.get('short'):
1537 if opts.get('short'):
1538 # if amending a patch, we start with existing
1538 # if amending a patch, we start with existing
1539 # files plus specified files - unfiltered
1539 # files plus specified files - unfiltered
1540 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1540 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1541 # filter with include/exclude options
1541 # filter with include/exclude options
1542 matchfn = scmutil.match(repo[None], opts=opts)
1542 matchfn = scmutil.match(repo[None], opts=opts)
1543 else:
1543 else:
1544 match = scmutil.matchall(repo)
1544 match = scmutil.matchall(repo)
1545 m, a, r, d = repo.status(match=match)[:4]
1545 m, a, r, d = repo.status(match=match)[:4]
1546 mm = set(mm)
1546 mm = set(mm)
1547 aa = set(aa)
1547 aa = set(aa)
1548 dd = set(dd)
1548 dd = set(dd)
1549
1549
1550 # we might end up with files that were added between
1550 # we might end up with files that were added between
1551 # qtip and the dirstate parent, but then changed in the
1551 # qtip and the dirstate parent, but then changed in the
1552 # local dirstate. in this case, we want them to only
1552 # local dirstate. in this case, we want them to only
1553 # show up in the added section
1553 # show up in the added section
1554 for x in m:
1554 for x in m:
1555 if x not in aa:
1555 if x not in aa:
1556 mm.add(x)
1556 mm.add(x)
1557 # we might end up with files added by the local dirstate that
1557 # we might end up with files added by the local dirstate that
1558 # were deleted by the patch. In this case, they should only
1558 # were deleted by the patch. In this case, they should only
1559 # show up in the changed section.
1559 # show up in the changed section.
1560 for x in a:
1560 for x in a:
1561 if x in dd:
1561 if x in dd:
1562 dd.remove(x)
1562 dd.remove(x)
1563 mm.add(x)
1563 mm.add(x)
1564 else:
1564 else:
1565 aa.add(x)
1565 aa.add(x)
1566 # make sure any files deleted in the local dirstate
1566 # make sure any files deleted in the local dirstate
1567 # are not in the add or change column of the patch
1567 # are not in the add or change column of the patch
1568 forget = []
1568 forget = []
1569 for x in d + r:
1569 for x in d + r:
1570 if x in aa:
1570 if x in aa:
1571 aa.remove(x)
1571 aa.remove(x)
1572 forget.append(x)
1572 forget.append(x)
1573 continue
1573 continue
1574 else:
1574 else:
1575 mm.discard(x)
1575 mm.discard(x)
1576 dd.add(x)
1576 dd.add(x)
1577
1577
1578 m = list(mm)
1578 m = list(mm)
1579 r = list(dd)
1579 r = list(dd)
1580 a = list(aa)
1580 a = list(aa)
1581
1581
1582 # create 'match' that includes the files to be recommitted.
1582 # create 'match' that includes the files to be recommitted.
1583 # apply matchfn via repo.status to ensure correct case handling.
1583 # apply matchfn via repo.status to ensure correct case handling.
1584 cm, ca, cr, cd = repo.status(patchparent, match=matchfn)[:4]
1584 cm, ca, cr, cd = repo.status(patchparent, match=matchfn)[:4]
1585 allmatches = set(cm + ca + cr + cd)
1585 allmatches = set(cm + ca + cr + cd)
1586 refreshchanges = [x.intersection(allmatches) for x in (mm, aa, dd)]
1586 refreshchanges = [x.intersection(allmatches) for x in (mm, aa, dd)]
1587
1587
1588 files = set(inclsubs)
1588 files = set(inclsubs)
1589 for x in refreshchanges:
1589 for x in refreshchanges:
1590 files.update(x)
1590 files.update(x)
1591 match = scmutil.matchfiles(repo, files)
1591 match = scmutil.matchfiles(repo, files)
1592
1592
1593 bmlist = repo[top].bookmarks()
1593 bmlist = repo[top].bookmarks()
1594
1594
1595 try:
1595 try:
1596 if diffopts.git or diffopts.upgrade:
1596 if diffopts.git or diffopts.upgrade:
1597 copies = {}
1597 copies = {}
1598 for dst in a:
1598 for dst in a:
1599 src = repo.dirstate.copied(dst)
1599 src = repo.dirstate.copied(dst)
1600 # during qfold, the source file for copies may
1600 # during qfold, the source file for copies may
1601 # be removed. Treat this as a simple add.
1601 # be removed. Treat this as a simple add.
1602 if src is not None and src in repo.dirstate:
1602 if src is not None and src in repo.dirstate:
1603 copies.setdefault(src, []).append(dst)
1603 copies.setdefault(src, []).append(dst)
1604 repo.dirstate.add(dst)
1604 repo.dirstate.add(dst)
1605 # remember the copies between patchparent and qtip
1605 # remember the copies between patchparent and qtip
1606 for dst in aaa:
1606 for dst in aaa:
1607 f = repo.file(dst)
1607 f = repo.file(dst)
1608 src = f.renamed(man[dst])
1608 src = f.renamed(man[dst])
1609 if src:
1609 if src:
1610 copies.setdefault(src[0], []).extend(
1610 copies.setdefault(src[0], []).extend(
1611 copies.get(dst, []))
1611 copies.get(dst, []))
1612 if dst in a:
1612 if dst in a:
1613 copies[src[0]].append(dst)
1613 copies[src[0]].append(dst)
1614 # we can't copy a file created by the patch itself
1614 # we can't copy a file created by the patch itself
1615 if dst in copies:
1615 if dst in copies:
1616 del copies[dst]
1616 del copies[dst]
1617 for src, dsts in copies.iteritems():
1617 for src, dsts in copies.iteritems():
1618 for dst in dsts:
1618 for dst in dsts:
1619 repo.dirstate.copy(src, dst)
1619 repo.dirstate.copy(src, dst)
1620 else:
1620 else:
1621 for dst in a:
1621 for dst in a:
1622 repo.dirstate.add(dst)
1622 repo.dirstate.add(dst)
1623 # Drop useless copy information
1623 # Drop useless copy information
1624 for f in list(repo.dirstate.copies()):
1624 for f in list(repo.dirstate.copies()):
1625 repo.dirstate.copy(None, f)
1625 repo.dirstate.copy(None, f)
1626 for f in r:
1626 for f in r:
1627 repo.dirstate.remove(f)
1627 repo.dirstate.remove(f)
1628 # if the patch excludes a modified file, mark that
1628 # if the patch excludes a modified file, mark that
1629 # file with mtime=0 so status can see it.
1629 # file with mtime=0 so status can see it.
1630 mm = []
1630 mm = []
1631 for i in xrange(len(m) - 1, -1, -1):
1631 for i in xrange(len(m) - 1, -1, -1):
1632 if not matchfn(m[i]):
1632 if not matchfn(m[i]):
1633 mm.append(m[i])
1633 mm.append(m[i])
1634 del m[i]
1634 del m[i]
1635 for f in m:
1635 for f in m:
1636 repo.dirstate.normal(f)
1636 repo.dirstate.normal(f)
1637 for f in mm:
1637 for f in mm:
1638 repo.dirstate.normallookup(f)
1638 repo.dirstate.normallookup(f)
1639 for f in forget:
1639 for f in forget:
1640 repo.dirstate.drop(f)
1640 repo.dirstate.drop(f)
1641
1641
1642 user = ph.user or changes[1]
1642 user = ph.user or changes[1]
1643
1643
1644 oldphase = repo[top].phase()
1644 oldphase = repo[top].phase()
1645
1645
1646 # assumes strip can roll itself back if interrupted
1646 # assumes strip can roll itself back if interrupted
1647 repo.setparents(*cparents)
1647 repo.setparents(*cparents)
1648 self.applied.pop()
1648 self.applied.pop()
1649 self.applieddirty = True
1649 self.applieddirty = True
1650 strip(self.ui, repo, [top], update=False, backup='strip')
1650 strip(self.ui, repo, [top], update=False, backup='strip')
1651 except: # re-raises
1651 except: # re-raises
1652 repo.dirstate.invalidate()
1652 repo.dirstate.invalidate()
1653 raise
1653 raise
1654
1654
1655 try:
1655 try:
1656 # might be nice to attempt to roll back strip after this
1656 # might be nice to attempt to roll back strip after this
1657
1657
1658 defaultmsg = "[mq]: %s" % patchfn
1658 defaultmsg = "[mq]: %s" % patchfn
1659 editor = cmdutil.getcommiteditor()
1659 editor = cmdutil.getcommiteditor()
1660 if edit:
1660 if edit:
1661 def finishdesc(desc):
1661 def finishdesc(desc):
1662 if desc.rstrip():
1662 if desc.rstrip():
1663 ph.setmessage(desc)
1663 ph.setmessage(desc)
1664 return desc
1664 return desc
1665 return defaultmsg
1665 return defaultmsg
1666 # i18n: this message is shown in editor with "HG: " prefix
1666 # i18n: this message is shown in editor with "HG: " prefix
1667 extramsg = _('Leave message empty to use default message.')
1667 extramsg = _('Leave message empty to use default message.')
1668 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1668 editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
1669 extramsg=extramsg)
1669 extramsg=extramsg)
1670 message = msg or "\n".join(ph.message)
1670 message = msg or "\n".join(ph.message)
1671 elif not msg:
1671 elif not msg:
1672 if not ph.message:
1672 if not ph.message:
1673 message = defaultmsg
1673 message = defaultmsg
1674 else:
1674 else:
1675 message = "\n".join(ph.message)
1675 message = "\n".join(ph.message)
1676 else:
1676 else:
1677 message = msg
1677 message = msg
1678 ph.setmessage(msg)
1678 ph.setmessage(msg)
1679
1679
1680 # Ensure we create a new changeset in the same phase than
1680 # Ensure we create a new changeset in the same phase than
1681 # the old one.
1681 # the old one.
1682 n = newcommit(repo, oldphase, message, user, ph.date,
1682 n = newcommit(repo, oldphase, message, user, ph.date,
1683 match=match, force=True, editor=editor)
1683 match=match, force=True, editor=editor)
1684 # only write patch after a successful commit
1684 # only write patch after a successful commit
1685 c = [list(x) for x in refreshchanges]
1685 c = [list(x) for x in refreshchanges]
1686 if inclsubs:
1686 if inclsubs:
1687 self.putsubstate2changes(substatestate, c)
1687 self.putsubstate2changes(substatestate, c)
1688 chunks = patchmod.diff(repo, patchparent,
1688 chunks = patchmod.diff(repo, patchparent,
1689 changes=c, opts=diffopts)
1689 changes=c, opts=diffopts)
1690 comments = str(ph)
1690 comments = str(ph)
1691 if comments:
1691 if comments:
1692 patchf.write(comments)
1692 patchf.write(comments)
1693 for chunk in chunks:
1693 for chunk in chunks:
1694 patchf.write(chunk)
1694 patchf.write(chunk)
1695 patchf.close()
1695 patchf.close()
1696
1696
1697 marks = repo._bookmarks
1697 marks = repo._bookmarks
1698 for bm in bmlist:
1698 for bm in bmlist:
1699 marks[bm] = n
1699 marks[bm] = n
1700 marks.write()
1700 marks.write()
1701
1701
1702 self.applied.append(statusentry(n, patchfn))
1702 self.applied.append(statusentry(n, patchfn))
1703 except: # re-raises
1703 except: # re-raises
1704 ctx = repo[cparents[0]]
1704 ctx = repo[cparents[0]]
1705 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1705 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1706 self.savedirty()
1706 self.savedirty()
1707 self.ui.warn(_('refresh interrupted while patch was popped! '
1707 self.ui.warn(_('refresh interrupted while patch was popped! '
1708 '(revert --all, qpush to recover)\n'))
1708 '(revert --all, qpush to recover)\n'))
1709 raise
1709 raise
1710 finally:
1710 finally:
1711 wlock.release()
1711 wlock.release()
1712 self.removeundo(repo)
1712 self.removeundo(repo)
1713
1713
1714 def init(self, repo, create=False):
1714 def init(self, repo, create=False):
1715 if not create and os.path.isdir(self.path):
1715 if not create and os.path.isdir(self.path):
1716 raise util.Abort(_("patch queue directory already exists"))
1716 raise util.Abort(_("patch queue directory already exists"))
1717 try:
1717 try:
1718 os.mkdir(self.path)
1718 os.mkdir(self.path)
1719 except OSError, inst:
1719 except OSError, inst:
1720 if inst.errno != errno.EEXIST or not create:
1720 if inst.errno != errno.EEXIST or not create:
1721 raise
1721 raise
1722 if create:
1722 if create:
1723 return self.qrepo(create=True)
1723 return self.qrepo(create=True)
1724
1724
1725 def unapplied(self, repo, patch=None):
1725 def unapplied(self, repo, patch=None):
1726 if patch and patch not in self.series:
1726 if patch and patch not in self.series:
1727 raise util.Abort(_("patch %s is not in series file") % patch)
1727 raise util.Abort(_("patch %s is not in series file") % patch)
1728 if not patch:
1728 if not patch:
1729 start = self.seriesend()
1729 start = self.seriesend()
1730 else:
1730 else:
1731 start = self.series.index(patch) + 1
1731 start = self.series.index(patch) + 1
1732 unapplied = []
1732 unapplied = []
1733 for i in xrange(start, len(self.series)):
1733 for i in xrange(start, len(self.series)):
1734 pushable, reason = self.pushable(i)
1734 pushable, reason = self.pushable(i)
1735 if pushable:
1735 if pushable:
1736 unapplied.append((i, self.series[i]))
1736 unapplied.append((i, self.series[i]))
1737 self.explainpushable(i)
1737 self.explainpushable(i)
1738 return unapplied
1738 return unapplied
1739
1739
1740 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1740 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1741 summary=False):
1741 summary=False):
1742 def displayname(pfx, patchname, state):
1742 def displayname(pfx, patchname, state):
1743 if pfx:
1743 if pfx:
1744 self.ui.write(pfx)
1744 self.ui.write(pfx)
1745 if summary:
1745 if summary:
1746 ph = patchheader(self.join(patchname), self.plainmode)
1746 ph = patchheader(self.join(patchname), self.plainmode)
1747 msg = ph.message and ph.message[0] or ''
1747 msg = ph.message and ph.message[0] or ''
1748 if self.ui.formatted():
1748 if self.ui.formatted():
1749 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1749 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1750 if width > 0:
1750 if width > 0:
1751 msg = util.ellipsis(msg, width)
1751 msg = util.ellipsis(msg, width)
1752 else:
1752 else:
1753 msg = ''
1753 msg = ''
1754 self.ui.write(patchname, label='qseries.' + state)
1754 self.ui.write(patchname, label='qseries.' + state)
1755 self.ui.write(': ')
1755 self.ui.write(': ')
1756 self.ui.write(msg, label='qseries.message.' + state)
1756 self.ui.write(msg, label='qseries.message.' + state)
1757 else:
1757 else:
1758 self.ui.write(patchname, label='qseries.' + state)
1758 self.ui.write(patchname, label='qseries.' + state)
1759 self.ui.write('\n')
1759 self.ui.write('\n')
1760
1760
1761 applied = set([p.name for p in self.applied])
1761 applied = set([p.name for p in self.applied])
1762 if length is None:
1762 if length is None:
1763 length = len(self.series) - start
1763 length = len(self.series) - start
1764 if not missing:
1764 if not missing:
1765 if self.ui.verbose:
1765 if self.ui.verbose:
1766 idxwidth = len(str(start + length - 1))
1766 idxwidth = len(str(start + length - 1))
1767 for i in xrange(start, start + length):
1767 for i in xrange(start, start + length):
1768 patch = self.series[i]
1768 patch = self.series[i]
1769 if patch in applied:
1769 if patch in applied:
1770 char, state = 'A', 'applied'
1770 char, state = 'A', 'applied'
1771 elif self.pushable(i)[0]:
1771 elif self.pushable(i)[0]:
1772 char, state = 'U', 'unapplied'
1772 char, state = 'U', 'unapplied'
1773 else:
1773 else:
1774 char, state = 'G', 'guarded'
1774 char, state = 'G', 'guarded'
1775 pfx = ''
1775 pfx = ''
1776 if self.ui.verbose:
1776 if self.ui.verbose:
1777 pfx = '%*d %s ' % (idxwidth, i, char)
1777 pfx = '%*d %s ' % (idxwidth, i, char)
1778 elif status and status != char:
1778 elif status and status != char:
1779 continue
1779 continue
1780 displayname(pfx, patch, state)
1780 displayname(pfx, patch, state)
1781 else:
1781 else:
1782 msng_list = []
1782 msng_list = []
1783 for root, dirs, files in os.walk(self.path):
1783 for root, dirs, files in os.walk(self.path):
1784 d = root[len(self.path) + 1:]
1784 d = root[len(self.path) + 1:]
1785 for f in files:
1785 for f in files:
1786 fl = os.path.join(d, f)
1786 fl = os.path.join(d, f)
1787 if (fl not in self.series and
1787 if (fl not in self.series and
1788 fl not in (self.statuspath, self.seriespath,
1788 fl not in (self.statuspath, self.seriespath,
1789 self.guardspath)
1789 self.guardspath)
1790 and not fl.startswith('.')):
1790 and not fl.startswith('.')):
1791 msng_list.append(fl)
1791 msng_list.append(fl)
1792 for x in sorted(msng_list):
1792 for x in sorted(msng_list):
1793 pfx = self.ui.verbose and ('D ') or ''
1793 pfx = self.ui.verbose and ('D ') or ''
1794 displayname(pfx, x, 'missing')
1794 displayname(pfx, x, 'missing')
1795
1795
1796 def issaveline(self, l):
1796 def issaveline(self, l):
1797 if l.name == '.hg.patches.save.line':
1797 if l.name == '.hg.patches.save.line':
1798 return True
1798 return True
1799
1799
1800 def qrepo(self, create=False):
1800 def qrepo(self, create=False):
1801 ui = self.baseui.copy()
1801 ui = self.baseui.copy()
1802 if create or os.path.isdir(self.join(".hg")):
1802 if create or os.path.isdir(self.join(".hg")):
1803 return hg.repository(ui, path=self.path, create=create)
1803 return hg.repository(ui, path=self.path, create=create)
1804
1804
1805 def restore(self, repo, rev, delete=None, qupdate=None):
1805 def restore(self, repo, rev, delete=None, qupdate=None):
1806 desc = repo[rev].description().strip()
1806 desc = repo[rev].description().strip()
1807 lines = desc.splitlines()
1807 lines = desc.splitlines()
1808 i = 0
1808 i = 0
1809 datastart = None
1809 datastart = None
1810 series = []
1810 series = []
1811 applied = []
1811 applied = []
1812 qpp = None
1812 qpp = None
1813 for i, line in enumerate(lines):
1813 for i, line in enumerate(lines):
1814 if line == 'Patch Data:':
1814 if line == 'Patch Data:':
1815 datastart = i + 1
1815 datastart = i + 1
1816 elif line.startswith('Dirstate:'):
1816 elif line.startswith('Dirstate:'):
1817 l = line.rstrip()
1817 l = line.rstrip()
1818 l = l[10:].split(' ')
1818 l = l[10:].split(' ')
1819 qpp = [bin(x) for x in l]
1819 qpp = [bin(x) for x in l]
1820 elif datastart is not None:
1820 elif datastart is not None:
1821 l = line.rstrip()
1821 l = line.rstrip()
1822 n, name = l.split(':', 1)
1822 n, name = l.split(':', 1)
1823 if n:
1823 if n:
1824 applied.append(statusentry(bin(n), name))
1824 applied.append(statusentry(bin(n), name))
1825 else:
1825 else:
1826 series.append(l)
1826 series.append(l)
1827 if datastart is None:
1827 if datastart is None:
1828 self.ui.warn(_("no saved patch data found\n"))
1828 self.ui.warn(_("no saved patch data found\n"))
1829 return 1
1829 return 1
1830 self.ui.warn(_("restoring status: %s\n") % lines[0])
1830 self.ui.warn(_("restoring status: %s\n") % lines[0])
1831 self.fullseries = series
1831 self.fullseries = series
1832 self.applied = applied
1832 self.applied = applied
1833 self.parseseries()
1833 self.parseseries()
1834 self.seriesdirty = True
1834 self.seriesdirty = True
1835 self.applieddirty = True
1835 self.applieddirty = True
1836 heads = repo.changelog.heads()
1836 heads = repo.changelog.heads()
1837 if delete:
1837 if delete:
1838 if rev not in heads:
1838 if rev not in heads:
1839 self.ui.warn(_("save entry has children, leaving it alone\n"))
1839 self.ui.warn(_("save entry has children, leaving it alone\n"))
1840 else:
1840 else:
1841 self.ui.warn(_("removing save entry %s\n") % short(rev))
1841 self.ui.warn(_("removing save entry %s\n") % short(rev))
1842 pp = repo.dirstate.parents()
1842 pp = repo.dirstate.parents()
1843 if rev in pp:
1843 if rev in pp:
1844 update = True
1844 update = True
1845 else:
1845 else:
1846 update = False
1846 update = False
1847 strip(self.ui, repo, [rev], update=update, backup='strip')
1847 strip(self.ui, repo, [rev], update=update, backup='strip')
1848 if qpp:
1848 if qpp:
1849 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1849 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1850 (short(qpp[0]), short(qpp[1])))
1850 (short(qpp[0]), short(qpp[1])))
1851 if qupdate:
1851 if qupdate:
1852 self.ui.status(_("updating queue directory\n"))
1852 self.ui.status(_("updating queue directory\n"))
1853 r = self.qrepo()
1853 r = self.qrepo()
1854 if not r:
1854 if not r:
1855 self.ui.warn(_("unable to load queue repository\n"))
1855 self.ui.warn(_("unable to load queue repository\n"))
1856 return 1
1856 return 1
1857 hg.clean(r, qpp[0])
1857 hg.clean(r, qpp[0])
1858
1858
1859 def save(self, repo, msg=None):
1859 def save(self, repo, msg=None):
1860 if not self.applied:
1860 if not self.applied:
1861 self.ui.warn(_("save: no patches applied, exiting\n"))
1861 self.ui.warn(_("save: no patches applied, exiting\n"))
1862 return 1
1862 return 1
1863 if self.issaveline(self.applied[-1]):
1863 if self.issaveline(self.applied[-1]):
1864 self.ui.warn(_("status is already saved\n"))
1864 self.ui.warn(_("status is already saved\n"))
1865 return 1
1865 return 1
1866
1866
1867 if not msg:
1867 if not msg:
1868 msg = _("hg patches saved state")
1868 msg = _("hg patches saved state")
1869 else:
1869 else:
1870 msg = "hg patches: " + msg.rstrip('\r\n')
1870 msg = "hg patches: " + msg.rstrip('\r\n')
1871 r = self.qrepo()
1871 r = self.qrepo()
1872 if r:
1872 if r:
1873 pp = r.dirstate.parents()
1873 pp = r.dirstate.parents()
1874 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1874 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1875 msg += "\n\nPatch Data:\n"
1875 msg += "\n\nPatch Data:\n"
1876 msg += ''.join('%s\n' % x for x in self.applied)
1876 msg += ''.join('%s\n' % x for x in self.applied)
1877 msg += ''.join(':%s\n' % x for x in self.fullseries)
1877 msg += ''.join(':%s\n' % x for x in self.fullseries)
1878 n = repo.commit(msg, force=True)
1878 n = repo.commit(msg, force=True)
1879 if not n:
1879 if not n:
1880 self.ui.warn(_("repo commit failed\n"))
1880 self.ui.warn(_("repo commit failed\n"))
1881 return 1
1881 return 1
1882 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1882 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1883 self.applieddirty = True
1883 self.applieddirty = True
1884 self.removeundo(repo)
1884 self.removeundo(repo)
1885
1885
1886 def fullseriesend(self):
1886 def fullseriesend(self):
1887 if self.applied:
1887 if self.applied:
1888 p = self.applied[-1].name
1888 p = self.applied[-1].name
1889 end = self.findseries(p)
1889 end = self.findseries(p)
1890 if end is None:
1890 if end is None:
1891 return len(self.fullseries)
1891 return len(self.fullseries)
1892 return end + 1
1892 return end + 1
1893 return 0
1893 return 0
1894
1894
1895 def seriesend(self, all_patches=False):
1895 def seriesend(self, all_patches=False):
1896 """If all_patches is False, return the index of the next pushable patch
1896 """If all_patches is False, return the index of the next pushable patch
1897 in the series, or the series length. If all_patches is True, return the
1897 in the series, or the series length. If all_patches is True, return the
1898 index of the first patch past the last applied one.
1898 index of the first patch past the last applied one.
1899 """
1899 """
1900 end = 0
1900 end = 0
1901 def nextpatch(start):
1901 def nextpatch(start):
1902 if all_patches or start >= len(self.series):
1902 if all_patches or start >= len(self.series):
1903 return start
1903 return start
1904 for i in xrange(start, len(self.series)):
1904 for i in xrange(start, len(self.series)):
1905 p, reason = self.pushable(i)
1905 p, reason = self.pushable(i)
1906 if p:
1906 if p:
1907 return i
1907 return i
1908 self.explainpushable(i)
1908 self.explainpushable(i)
1909 return len(self.series)
1909 return len(self.series)
1910 if self.applied:
1910 if self.applied:
1911 p = self.applied[-1].name
1911 p = self.applied[-1].name
1912 try:
1912 try:
1913 end = self.series.index(p)
1913 end = self.series.index(p)
1914 except ValueError:
1914 except ValueError:
1915 return 0
1915 return 0
1916 return nextpatch(end + 1)
1916 return nextpatch(end + 1)
1917 return nextpatch(end)
1917 return nextpatch(end)
1918
1918
1919 def appliedname(self, index):
1919 def appliedname(self, index):
1920 pname = self.applied[index].name
1920 pname = self.applied[index].name
1921 if not self.ui.verbose:
1921 if not self.ui.verbose:
1922 p = pname
1922 p = pname
1923 else:
1923 else:
1924 p = str(self.series.index(pname)) + " " + pname
1924 p = str(self.series.index(pname)) + " " + pname
1925 return p
1925 return p
1926
1926
1927 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1927 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1928 force=None, git=False):
1928 force=None, git=False):
1929 def checkseries(patchname):
1929 def checkseries(patchname):
1930 if patchname in self.series:
1930 if patchname in self.series:
1931 raise util.Abort(_('patch %s is already in the series file')
1931 raise util.Abort(_('patch %s is already in the series file')
1932 % patchname)
1932 % patchname)
1933
1933
1934 if rev:
1934 if rev:
1935 if files:
1935 if files:
1936 raise util.Abort(_('option "-r" not valid when importing '
1936 raise util.Abort(_('option "-r" not valid when importing '
1937 'files'))
1937 'files'))
1938 rev = scmutil.revrange(repo, rev)
1938 rev = scmutil.revrange(repo, rev)
1939 rev.sort(reverse=True)
1939 rev.sort(reverse=True)
1940 elif not files:
1940 elif not files:
1941 raise util.Abort(_('no files or revisions specified'))
1941 raise util.Abort(_('no files or revisions specified'))
1942 if (len(files) > 1 or len(rev) > 1) and patchname:
1942 if (len(files) > 1 or len(rev) > 1) and patchname:
1943 raise util.Abort(_('option "-n" not valid when importing multiple '
1943 raise util.Abort(_('option "-n" not valid when importing multiple '
1944 'patches'))
1944 'patches'))
1945 imported = []
1945 imported = []
1946 if rev:
1946 if rev:
1947 # If mq patches are applied, we can only import revisions
1947 # If mq patches are applied, we can only import revisions
1948 # that form a linear path to qbase.
1948 # that form a linear path to qbase.
1949 # Otherwise, they should form a linear path to a head.
1949 # Otherwise, they should form a linear path to a head.
1950 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1950 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1951 if len(heads) > 1:
1951 if len(heads) > 1:
1952 raise util.Abort(_('revision %d is the root of more than one '
1952 raise util.Abort(_('revision %d is the root of more than one '
1953 'branch') % rev[-1])
1953 'branch') % rev[-1])
1954 if self.applied:
1954 if self.applied:
1955 base = repo.changelog.node(rev[0])
1955 base = repo.changelog.node(rev[0])
1956 if base in [n.node for n in self.applied]:
1956 if base in [n.node for n in self.applied]:
1957 raise util.Abort(_('revision %d is already managed')
1957 raise util.Abort(_('revision %d is already managed')
1958 % rev[0])
1958 % rev[0])
1959 if heads != [self.applied[-1].node]:
1959 if heads != [self.applied[-1].node]:
1960 raise util.Abort(_('revision %d is not the parent of '
1960 raise util.Abort(_('revision %d is not the parent of '
1961 'the queue') % rev[0])
1961 'the queue') % rev[0])
1962 base = repo.changelog.rev(self.applied[0].node)
1962 base = repo.changelog.rev(self.applied[0].node)
1963 lastparent = repo.changelog.parentrevs(base)[0]
1963 lastparent = repo.changelog.parentrevs(base)[0]
1964 else:
1964 else:
1965 if heads != [repo.changelog.node(rev[0])]:
1965 if heads != [repo.changelog.node(rev[0])]:
1966 raise util.Abort(_('revision %d has unmanaged children')
1966 raise util.Abort(_('revision %d has unmanaged children')
1967 % rev[0])
1967 % rev[0])
1968 lastparent = None
1968 lastparent = None
1969
1969
1970 diffopts = self.diffopts({'git': git})
1970 diffopts = self.diffopts({'git': git})
1971 for r in rev:
1971 for r in rev:
1972 if not repo[r].mutable():
1972 if not repo[r].mutable():
1973 raise util.Abort(_('revision %d is not mutable') % r,
1973 raise util.Abort(_('revision %d is not mutable') % r,
1974 hint=_('see "hg help phases" for details'))
1974 hint=_('see "hg help phases" for details'))
1975 p1, p2 = repo.changelog.parentrevs(r)
1975 p1, p2 = repo.changelog.parentrevs(r)
1976 n = repo.changelog.node(r)
1976 n = repo.changelog.node(r)
1977 if p2 != nullrev:
1977 if p2 != nullrev:
1978 raise util.Abort(_('cannot import merge revision %d') % r)
1978 raise util.Abort(_('cannot import merge revision %d') % r)
1979 if lastparent and lastparent != r:
1979 if lastparent and lastparent != r:
1980 raise util.Abort(_('revision %d is not the parent of %d')
1980 raise util.Abort(_('revision %d is not the parent of %d')
1981 % (r, lastparent))
1981 % (r, lastparent))
1982 lastparent = p1
1982 lastparent = p1
1983
1983
1984 if not patchname:
1984 if not patchname:
1985 patchname = normname('%d.diff' % r)
1985 patchname = normname('%d.diff' % r)
1986 checkseries(patchname)
1986 checkseries(patchname)
1987 self.checkpatchname(patchname, force)
1987 self.checkpatchname(patchname, force)
1988 self.fullseries.insert(0, patchname)
1988 self.fullseries.insert(0, patchname)
1989
1989
1990 patchf = self.opener(patchname, "w")
1990 patchf = self.opener(patchname, "w")
1991 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1991 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1992 patchf.close()
1992 patchf.close()
1993
1993
1994 se = statusentry(n, patchname)
1994 se = statusentry(n, patchname)
1995 self.applied.insert(0, se)
1995 self.applied.insert(0, se)
1996
1996
1997 self.added.append(patchname)
1997 self.added.append(patchname)
1998 imported.append(patchname)
1998 imported.append(patchname)
1999 patchname = None
1999 patchname = None
2000 if rev and repo.ui.configbool('mq', 'secret', False):
2000 if rev and repo.ui.configbool('mq', 'secret', False):
2001 # if we added anything with --rev, we must move the secret root
2001 # if we added anything with --rev, we must move the secret root
2002 phases.retractboundary(repo, phases.secret, [n])
2002 phases.retractboundary(repo, phases.secret, [n])
2003 self.parseseries()
2003 self.parseseries()
2004 self.applieddirty = True
2004 self.applieddirty = True
2005 self.seriesdirty = True
2005 self.seriesdirty = True
2006
2006
2007 for i, filename in enumerate(files):
2007 for i, filename in enumerate(files):
2008 if existing:
2008 if existing:
2009 if filename == '-':
2009 if filename == '-':
2010 raise util.Abort(_('-e is incompatible with import from -'))
2010 raise util.Abort(_('-e is incompatible with import from -'))
2011 filename = normname(filename)
2011 filename = normname(filename)
2012 self.checkreservedname(filename)
2012 self.checkreservedname(filename)
2013 if util.url(filename).islocal():
2013 if util.url(filename).islocal():
2014 originpath = self.join(filename)
2014 originpath = self.join(filename)
2015 if not os.path.isfile(originpath):
2015 if not os.path.isfile(originpath):
2016 raise util.Abort(
2016 raise util.Abort(
2017 _("patch %s does not exist") % filename)
2017 _("patch %s does not exist") % filename)
2018
2018
2019 if patchname:
2019 if patchname:
2020 self.checkpatchname(patchname, force)
2020 self.checkpatchname(patchname, force)
2021
2021
2022 self.ui.write(_('renaming %s to %s\n')
2022 self.ui.write(_('renaming %s to %s\n')
2023 % (filename, patchname))
2023 % (filename, patchname))
2024 util.rename(originpath, self.join(patchname))
2024 util.rename(originpath, self.join(patchname))
2025 else:
2025 else:
2026 patchname = filename
2026 patchname = filename
2027
2027
2028 else:
2028 else:
2029 if filename == '-' and not patchname:
2029 if filename == '-' and not patchname:
2030 raise util.Abort(_('need --name to import a patch from -'))
2030 raise util.Abort(_('need --name to import a patch from -'))
2031 elif not patchname:
2031 elif not patchname:
2032 patchname = normname(os.path.basename(filename.rstrip('/')))
2032 patchname = normname(os.path.basename(filename.rstrip('/')))
2033 self.checkpatchname(patchname, force)
2033 self.checkpatchname(patchname, force)
2034 try:
2034 try:
2035 if filename == '-':
2035 if filename == '-':
2036 text = self.ui.fin.read()
2036 text = self.ui.fin.read()
2037 else:
2037 else:
2038 fp = hg.openpath(self.ui, filename)
2038 fp = hg.openpath(self.ui, filename)
2039 text = fp.read()
2039 text = fp.read()
2040 fp.close()
2040 fp.close()
2041 except (OSError, IOError):
2041 except (OSError, IOError):
2042 raise util.Abort(_("unable to read file %s") % filename)
2042 raise util.Abort(_("unable to read file %s") % filename)
2043 patchf = self.opener(patchname, "w")
2043 patchf = self.opener(patchname, "w")
2044 patchf.write(text)
2044 patchf.write(text)
2045 patchf.close()
2045 patchf.close()
2046 if not force:
2046 if not force:
2047 checkseries(patchname)
2047 checkseries(patchname)
2048 if patchname not in self.series:
2048 if patchname not in self.series:
2049 index = self.fullseriesend() + i
2049 index = self.fullseriesend() + i
2050 self.fullseries[index:index] = [patchname]
2050 self.fullseries[index:index] = [patchname]
2051 self.parseseries()
2051 self.parseseries()
2052 self.seriesdirty = True
2052 self.seriesdirty = True
2053 self.ui.warn(_("adding %s to series file\n") % patchname)
2053 self.ui.warn(_("adding %s to series file\n") % patchname)
2054 self.added.append(patchname)
2054 self.added.append(patchname)
2055 imported.append(patchname)
2055 imported.append(patchname)
2056 patchname = None
2056 patchname = None
2057
2057
2058 self.removeundo(repo)
2058 self.removeundo(repo)
2059 return imported
2059 return imported
2060
2060
2061 def fixkeepchangesopts(ui, opts):
2061 def fixkeepchangesopts(ui, opts):
2062 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2062 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2063 or opts.get('exact')):
2063 or opts.get('exact')):
2064 return opts
2064 return opts
2065 opts = dict(opts)
2065 opts = dict(opts)
2066 opts['keep_changes'] = True
2066 opts['keep_changes'] = True
2067 return opts
2067 return opts
2068
2068
2069 @command("qdelete|qremove|qrm",
2069 @command("qdelete|qremove|qrm",
2070 [('k', 'keep', None, _('keep patch file')),
2070 [('k', 'keep', None, _('keep patch file')),
2071 ('r', 'rev', [],
2071 ('r', 'rev', [],
2072 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2072 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2073 _('hg qdelete [-k] [PATCH]...'))
2073 _('hg qdelete [-k] [PATCH]...'))
2074 def delete(ui, repo, *patches, **opts):
2074 def delete(ui, repo, *patches, **opts):
2075 """remove patches from queue
2075 """remove patches from queue
2076
2076
2077 The patches must not be applied, and at least one patch is required. Exact
2077 The patches must not be applied, and at least one patch is required. Exact
2078 patch identifiers must be given. With -k/--keep, the patch files are
2078 patch identifiers must be given. With -k/--keep, the patch files are
2079 preserved in the patch directory.
2079 preserved in the patch directory.
2080
2080
2081 To stop managing a patch and move it into permanent history,
2081 To stop managing a patch and move it into permanent history,
2082 use the :hg:`qfinish` command."""
2082 use the :hg:`qfinish` command."""
2083 q = repo.mq
2083 q = repo.mq
2084 q.delete(repo, patches, opts)
2084 q.delete(repo, patches, opts)
2085 q.savedirty()
2085 q.savedirty()
2086 return 0
2086 return 0
2087
2087
2088 @command("qapplied",
2088 @command("qapplied",
2089 [('1', 'last', None, _('show only the preceding applied patch'))
2089 [('1', 'last', None, _('show only the preceding applied patch'))
2090 ] + seriesopts,
2090 ] + seriesopts,
2091 _('hg qapplied [-1] [-s] [PATCH]'))
2091 _('hg qapplied [-1] [-s] [PATCH]'))
2092 def applied(ui, repo, patch=None, **opts):
2092 def applied(ui, repo, patch=None, **opts):
2093 """print the patches already applied
2093 """print the patches already applied
2094
2094
2095 Returns 0 on success."""
2095 Returns 0 on success."""
2096
2096
2097 q = repo.mq
2097 q = repo.mq
2098
2098
2099 if patch:
2099 if patch:
2100 if patch not in q.series:
2100 if patch not in q.series:
2101 raise util.Abort(_("patch %s is not in series file") % patch)
2101 raise util.Abort(_("patch %s is not in series file") % patch)
2102 end = q.series.index(patch) + 1
2102 end = q.series.index(patch) + 1
2103 else:
2103 else:
2104 end = q.seriesend(True)
2104 end = q.seriesend(True)
2105
2105
2106 if opts.get('last') and not end:
2106 if opts.get('last') and not end:
2107 ui.write(_("no patches applied\n"))
2107 ui.write(_("no patches applied\n"))
2108 return 1
2108 return 1
2109 elif opts.get('last') and end == 1:
2109 elif opts.get('last') and end == 1:
2110 ui.write(_("only one patch applied\n"))
2110 ui.write(_("only one patch applied\n"))
2111 return 1
2111 return 1
2112 elif opts.get('last'):
2112 elif opts.get('last'):
2113 start = end - 2
2113 start = end - 2
2114 end = 1
2114 end = 1
2115 else:
2115 else:
2116 start = 0
2116 start = 0
2117
2117
2118 q.qseries(repo, length=end, start=start, status='A',
2118 q.qseries(repo, length=end, start=start, status='A',
2119 summary=opts.get('summary'))
2119 summary=opts.get('summary'))
2120
2120
2121
2121
2122 @command("qunapplied",
2122 @command("qunapplied",
2123 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2123 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2124 _('hg qunapplied [-1] [-s] [PATCH]'))
2124 _('hg qunapplied [-1] [-s] [PATCH]'))
2125 def unapplied(ui, repo, patch=None, **opts):
2125 def unapplied(ui, repo, patch=None, **opts):
2126 """print the patches not yet applied
2126 """print the patches not yet applied
2127
2127
2128 Returns 0 on success."""
2128 Returns 0 on success."""
2129
2129
2130 q = repo.mq
2130 q = repo.mq
2131 if patch:
2131 if patch:
2132 if patch not in q.series:
2132 if patch not in q.series:
2133 raise util.Abort(_("patch %s is not in series file") % patch)
2133 raise util.Abort(_("patch %s is not in series file") % patch)
2134 start = q.series.index(patch) + 1
2134 start = q.series.index(patch) + 1
2135 else:
2135 else:
2136 start = q.seriesend(True)
2136 start = q.seriesend(True)
2137
2137
2138 if start == len(q.series) and opts.get('first'):
2138 if start == len(q.series) and opts.get('first'):
2139 ui.write(_("all patches applied\n"))
2139 ui.write(_("all patches applied\n"))
2140 return 1
2140 return 1
2141
2141
2142 length = opts.get('first') and 1 or None
2142 length = opts.get('first') and 1 or None
2143 q.qseries(repo, start=start, length=length, status='U',
2143 q.qseries(repo, start=start, length=length, status='U',
2144 summary=opts.get('summary'))
2144 summary=opts.get('summary'))
2145
2145
2146 @command("qimport",
2146 @command("qimport",
2147 [('e', 'existing', None, _('import file in patch directory')),
2147 [('e', 'existing', None, _('import file in patch directory')),
2148 ('n', 'name', '',
2148 ('n', 'name', '',
2149 _('name of patch file'), _('NAME')),
2149 _('name of patch file'), _('NAME')),
2150 ('f', 'force', None, _('overwrite existing files')),
2150 ('f', 'force', None, _('overwrite existing files')),
2151 ('r', 'rev', [],
2151 ('r', 'rev', [],
2152 _('place existing revisions under mq control'), _('REV')),
2152 _('place existing revisions under mq control'), _('REV')),
2153 ('g', 'git', None, _('use git extended diff format')),
2153 ('g', 'git', None, _('use git extended diff format')),
2154 ('P', 'push', None, _('qpush after importing'))],
2154 ('P', 'push', None, _('qpush after importing'))],
2155 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2155 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2156 def qimport(ui, repo, *filename, **opts):
2156 def qimport(ui, repo, *filename, **opts):
2157 """import a patch or existing changeset
2157 """import a patch or existing changeset
2158
2158
2159 The patch is inserted into the series after the last applied
2159 The patch is inserted into the series after the last applied
2160 patch. If no patches have been applied, qimport prepends the patch
2160 patch. If no patches have been applied, qimport prepends the patch
2161 to the series.
2161 to the series.
2162
2162
2163 The patch will have the same name as its source file unless you
2163 The patch will have the same name as its source file unless you
2164 give it a new one with -n/--name.
2164 give it a new one with -n/--name.
2165
2165
2166 You can register an existing patch inside the patch directory with
2166 You can register an existing patch inside the patch directory with
2167 the -e/--existing flag.
2167 the -e/--existing flag.
2168
2168
2169 With -f/--force, an existing patch of the same name will be
2169 With -f/--force, an existing patch of the same name will be
2170 overwritten.
2170 overwritten.
2171
2171
2172 An existing changeset may be placed under mq control with -r/--rev
2172 An existing changeset may be placed under mq control with -r/--rev
2173 (e.g. qimport --rev . -n patch will place the current revision
2173 (e.g. qimport --rev . -n patch will place the current revision
2174 under mq control). With -g/--git, patches imported with --rev will
2174 under mq control). With -g/--git, patches imported with --rev will
2175 use the git diff format. See the diffs help topic for information
2175 use the git diff format. See the diffs help topic for information
2176 on why this is important for preserving rename/copy information
2176 on why this is important for preserving rename/copy information
2177 and permission changes. Use :hg:`qfinish` to remove changesets
2177 and permission changes. Use :hg:`qfinish` to remove changesets
2178 from mq control.
2178 from mq control.
2179
2179
2180 To import a patch from standard input, pass - as the patch file.
2180 To import a patch from standard input, pass - as the patch file.
2181 When importing from standard input, a patch name must be specified
2181 When importing from standard input, a patch name must be specified
2182 using the --name flag.
2182 using the --name flag.
2183
2183
2184 To import an existing patch while renaming it::
2184 To import an existing patch while renaming it::
2185
2185
2186 hg qimport -e existing-patch -n new-name
2186 hg qimport -e existing-patch -n new-name
2187
2187
2188 Returns 0 if import succeeded.
2188 Returns 0 if import succeeded.
2189 """
2189 """
2190 lock = repo.lock() # cause this may move phase
2190 lock = repo.lock() # cause this may move phase
2191 try:
2191 try:
2192 q = repo.mq
2192 q = repo.mq
2193 try:
2193 try:
2194 imported = q.qimport(
2194 imported = q.qimport(
2195 repo, filename, patchname=opts.get('name'),
2195 repo, filename, patchname=opts.get('name'),
2196 existing=opts.get('existing'), force=opts.get('force'),
2196 existing=opts.get('existing'), force=opts.get('force'),
2197 rev=opts.get('rev'), git=opts.get('git'))
2197 rev=opts.get('rev'), git=opts.get('git'))
2198 finally:
2198 finally:
2199 q.savedirty()
2199 q.savedirty()
2200 finally:
2200 finally:
2201 lock.release()
2201 lock.release()
2202
2202
2203 if imported and opts.get('push') and not opts.get('rev'):
2203 if imported and opts.get('push') and not opts.get('rev'):
2204 return q.push(repo, imported[-1])
2204 return q.push(repo, imported[-1])
2205 return 0
2205 return 0
2206
2206
2207 def qinit(ui, repo, create):
2207 def qinit(ui, repo, create):
2208 """initialize a new queue repository
2208 """initialize a new queue repository
2209
2209
2210 This command also creates a series file for ordering patches, and
2210 This command also creates a series file for ordering patches, and
2211 an mq-specific .hgignore file in the queue repository, to exclude
2211 an mq-specific .hgignore file in the queue repository, to exclude
2212 the status and guards files (these contain mostly transient state).
2212 the status and guards files (these contain mostly transient state).
2213
2213
2214 Returns 0 if initialization succeeded."""
2214 Returns 0 if initialization succeeded."""
2215 q = repo.mq
2215 q = repo.mq
2216 r = q.init(repo, create)
2216 r = q.init(repo, create)
2217 q.savedirty()
2217 q.savedirty()
2218 if r:
2218 if r:
2219 if not os.path.exists(r.wjoin('.hgignore')):
2219 if not os.path.exists(r.wjoin('.hgignore')):
2220 fp = r.wopener('.hgignore', 'w')
2220 fp = r.wopener('.hgignore', 'w')
2221 fp.write('^\\.hg\n')
2221 fp.write('^\\.hg\n')
2222 fp.write('^\\.mq\n')
2222 fp.write('^\\.mq\n')
2223 fp.write('syntax: glob\n')
2223 fp.write('syntax: glob\n')
2224 fp.write('status\n')
2224 fp.write('status\n')
2225 fp.write('guards\n')
2225 fp.write('guards\n')
2226 fp.close()
2226 fp.close()
2227 if not os.path.exists(r.wjoin('series')):
2227 if not os.path.exists(r.wjoin('series')):
2228 r.wopener('series', 'w').close()
2228 r.wopener('series', 'w').close()
2229 r[None].add(['.hgignore', 'series'])
2229 r[None].add(['.hgignore', 'series'])
2230 commands.add(ui, r)
2230 commands.add(ui, r)
2231 return 0
2231 return 0
2232
2232
2233 @command("^qinit",
2233 @command("^qinit",
2234 [('c', 'create-repo', None, _('create queue repository'))],
2234 [('c', 'create-repo', None, _('create queue repository'))],
2235 _('hg qinit [-c]'))
2235 _('hg qinit [-c]'))
2236 def init(ui, repo, **opts):
2236 def init(ui, repo, **opts):
2237 """init a new queue repository (DEPRECATED)
2237 """init a new queue repository (DEPRECATED)
2238
2238
2239 The queue repository is unversioned by default. If
2239 The queue repository is unversioned by default. If
2240 -c/--create-repo is specified, qinit will create a separate nested
2240 -c/--create-repo is specified, qinit will create a separate nested
2241 repository for patches (qinit -c may also be run later to convert
2241 repository for patches (qinit -c may also be run later to convert
2242 an unversioned patch repository into a versioned one). You can use
2242 an unversioned patch repository into a versioned one). You can use
2243 qcommit to commit changes to this queue repository.
2243 qcommit to commit changes to this queue repository.
2244
2244
2245 This command is deprecated. Without -c, it's implied by other relevant
2245 This command is deprecated. Without -c, it's implied by other relevant
2246 commands. With -c, use :hg:`init --mq` instead."""
2246 commands. With -c, use :hg:`init --mq` instead."""
2247 return qinit(ui, repo, create=opts.get('create_repo'))
2247 return qinit(ui, repo, create=opts.get('create_repo'))
2248
2248
2249 @command("qclone",
2249 @command("qclone",
2250 [('', 'pull', None, _('use pull protocol to copy metadata')),
2250 [('', 'pull', None, _('use pull protocol to copy metadata')),
2251 ('U', 'noupdate', None,
2251 ('U', 'noupdate', None,
2252 _('do not update the new working directories')),
2252 _('do not update the new working directories')),
2253 ('', 'uncompressed', None,
2253 ('', 'uncompressed', None,
2254 _('use uncompressed transfer (fast over LAN)')),
2254 _('use uncompressed transfer (fast over LAN)')),
2255 ('p', 'patches', '',
2255 ('p', 'patches', '',
2256 _('location of source patch repository'), _('REPO')),
2256 _('location of source patch repository'), _('REPO')),
2257 ] + commands.remoteopts,
2257 ] + commands.remoteopts,
2258 _('hg qclone [OPTION]... SOURCE [DEST]'))
2258 _('hg qclone [OPTION]... SOURCE [DEST]'))
2259 def clone(ui, source, dest=None, **opts):
2259 def clone(ui, source, dest=None, **opts):
2260 '''clone main and patch repository at same time
2260 '''clone main and patch repository at same time
2261
2261
2262 If source is local, destination will have no patches applied. If
2262 If source is local, destination will have no patches applied. If
2263 source is remote, this command can not check if patches are
2263 source is remote, this command can not check if patches are
2264 applied in source, so cannot guarantee that patches are not
2264 applied in source, so cannot guarantee that patches are not
2265 applied in destination. If you clone remote repository, be sure
2265 applied in destination. If you clone remote repository, be sure
2266 before that it has no patches applied.
2266 before that it has no patches applied.
2267
2267
2268 Source patch repository is looked for in <src>/.hg/patches by
2268 Source patch repository is looked for in <src>/.hg/patches by
2269 default. Use -p <url> to change.
2269 default. Use -p <url> to change.
2270
2270
2271 The patch directory must be a nested Mercurial repository, as
2271 The patch directory must be a nested Mercurial repository, as
2272 would be created by :hg:`init --mq`.
2272 would be created by :hg:`init --mq`.
2273
2273
2274 Return 0 on success.
2274 Return 0 on success.
2275 '''
2275 '''
2276 def patchdir(repo):
2276 def patchdir(repo):
2277 """compute a patch repo url from a repo object"""
2277 """compute a patch repo url from a repo object"""
2278 url = repo.url()
2278 url = repo.url()
2279 if url.endswith('/'):
2279 if url.endswith('/'):
2280 url = url[:-1]
2280 url = url[:-1]
2281 return url + '/.hg/patches'
2281 return url + '/.hg/patches'
2282
2282
2283 # main repo (destination and sources)
2283 # main repo (destination and sources)
2284 if dest is None:
2284 if dest is None:
2285 dest = hg.defaultdest(source)
2285 dest = hg.defaultdest(source)
2286 sr = hg.peer(ui, opts, ui.expandpath(source))
2286 sr = hg.peer(ui, opts, ui.expandpath(source))
2287
2287
2288 # patches repo (source only)
2288 # patches repo (source only)
2289 if opts.get('patches'):
2289 if opts.get('patches'):
2290 patchespath = ui.expandpath(opts.get('patches'))
2290 patchespath = ui.expandpath(opts.get('patches'))
2291 else:
2291 else:
2292 patchespath = patchdir(sr)
2292 patchespath = patchdir(sr)
2293 try:
2293 try:
2294 hg.peer(ui, opts, patchespath)
2294 hg.peer(ui, opts, patchespath)
2295 except error.RepoError:
2295 except error.RepoError:
2296 raise util.Abort(_('versioned patch repository not found'
2296 raise util.Abort(_('versioned patch repository not found'
2297 ' (see init --mq)'))
2297 ' (see init --mq)'))
2298 qbase, destrev = None, None
2298 qbase, destrev = None, None
2299 if sr.local():
2299 if sr.local():
2300 repo = sr.local()
2300 repo = sr.local()
2301 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2301 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2302 qbase = repo.mq.applied[0].node
2302 qbase = repo.mq.applied[0].node
2303 if not hg.islocal(dest):
2303 if not hg.islocal(dest):
2304 heads = set(repo.heads())
2304 heads = set(repo.heads())
2305 destrev = list(heads.difference(repo.heads(qbase)))
2305 destrev = list(heads.difference(repo.heads(qbase)))
2306 destrev.append(repo.changelog.parents(qbase)[0])
2306 destrev.append(repo.changelog.parents(qbase)[0])
2307 elif sr.capable('lookup'):
2307 elif sr.capable('lookup'):
2308 try:
2308 try:
2309 qbase = sr.lookup('qbase')
2309 qbase = sr.lookup('qbase')
2310 except error.RepoError:
2310 except error.RepoError:
2311 pass
2311 pass
2312
2312
2313 ui.note(_('cloning main repository\n'))
2313 ui.note(_('cloning main repository\n'))
2314 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2314 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2315 pull=opts.get('pull'),
2315 pull=opts.get('pull'),
2316 rev=destrev,
2316 rev=destrev,
2317 update=False,
2317 update=False,
2318 stream=opts.get('uncompressed'))
2318 stream=opts.get('uncompressed'))
2319
2319
2320 ui.note(_('cloning patch repository\n'))
2320 ui.note(_('cloning patch repository\n'))
2321 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2321 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2322 pull=opts.get('pull'), update=not opts.get('noupdate'),
2322 pull=opts.get('pull'), update=not opts.get('noupdate'),
2323 stream=opts.get('uncompressed'))
2323 stream=opts.get('uncompressed'))
2324
2324
2325 if dr.local():
2325 if dr.local():
2326 repo = dr.local()
2326 repo = dr.local()
2327 if qbase:
2327 if qbase:
2328 ui.note(_('stripping applied patches from destination '
2328 ui.note(_('stripping applied patches from destination '
2329 'repository\n'))
2329 'repository\n'))
2330 strip(ui, repo, [qbase], update=False, backup=None)
2330 strip(ui, repo, [qbase], update=False, backup=None)
2331 if not opts.get('noupdate'):
2331 if not opts.get('noupdate'):
2332 ui.note(_('updating destination repository\n'))
2332 ui.note(_('updating destination repository\n'))
2333 hg.update(repo, repo.changelog.tip())
2333 hg.update(repo, repo.changelog.tip())
2334
2334
2335 @command("qcommit|qci",
2335 @command("qcommit|qci",
2336 commands.table["^commit|ci"][1],
2336 commands.table["^commit|ci"][1],
2337 _('hg qcommit [OPTION]... [FILE]...'))
2337 _('hg qcommit [OPTION]... [FILE]...'))
2338 def commit(ui, repo, *pats, **opts):
2338 def commit(ui, repo, *pats, **opts):
2339 """commit changes in the queue repository (DEPRECATED)
2339 """commit changes in the queue repository (DEPRECATED)
2340
2340
2341 This command is deprecated; use :hg:`commit --mq` instead."""
2341 This command is deprecated; use :hg:`commit --mq` instead."""
2342 q = repo.mq
2342 q = repo.mq
2343 r = q.qrepo()
2343 r = q.qrepo()
2344 if not r:
2344 if not r:
2345 raise util.Abort('no queue repository')
2345 raise util.Abort('no queue repository')
2346 commands.commit(r.ui, r, *pats, **opts)
2346 commands.commit(r.ui, r, *pats, **opts)
2347
2347
2348 @command("qseries",
2348 @command("qseries",
2349 [('m', 'missing', None, _('print patches not in series')),
2349 [('m', 'missing', None, _('print patches not in series')),
2350 ] + seriesopts,
2350 ] + seriesopts,
2351 _('hg qseries [-ms]'))
2351 _('hg qseries [-ms]'))
2352 def series(ui, repo, **opts):
2352 def series(ui, repo, **opts):
2353 """print the entire series file
2353 """print the entire series file
2354
2354
2355 Returns 0 on success."""
2355 Returns 0 on success."""
2356 repo.mq.qseries(repo, missing=opts.get('missing'),
2356 repo.mq.qseries(repo, missing=opts.get('missing'),
2357 summary=opts.get('summary'))
2357 summary=opts.get('summary'))
2358 return 0
2358 return 0
2359
2359
2360 @command("qtop", seriesopts, _('hg qtop [-s]'))
2360 @command("qtop", seriesopts, _('hg qtop [-s]'))
2361 def top(ui, repo, **opts):
2361 def top(ui, repo, **opts):
2362 """print the name of the current patch
2362 """print the name of the current patch
2363
2363
2364 Returns 0 on success."""
2364 Returns 0 on success."""
2365 q = repo.mq
2365 q = repo.mq
2366 t = q.applied and q.seriesend(True) or 0
2366 t = q.applied and q.seriesend(True) or 0
2367 if t:
2367 if t:
2368 q.qseries(repo, start=t - 1, length=1, status='A',
2368 q.qseries(repo, start=t - 1, length=1, status='A',
2369 summary=opts.get('summary'))
2369 summary=opts.get('summary'))
2370 else:
2370 else:
2371 ui.write(_("no patches applied\n"))
2371 ui.write(_("no patches applied\n"))
2372 return 1
2372 return 1
2373
2373
2374 @command("qnext", seriesopts, _('hg qnext [-s]'))
2374 @command("qnext", seriesopts, _('hg qnext [-s]'))
2375 def next(ui, repo, **opts):
2375 def next(ui, repo, **opts):
2376 """print the name of the next pushable patch
2376 """print the name of the next pushable patch
2377
2377
2378 Returns 0 on success."""
2378 Returns 0 on success."""
2379 q = repo.mq
2379 q = repo.mq
2380 end = q.seriesend()
2380 end = q.seriesend()
2381 if end == len(q.series):
2381 if end == len(q.series):
2382 ui.write(_("all patches applied\n"))
2382 ui.write(_("all patches applied\n"))
2383 return 1
2383 return 1
2384 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2384 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2385
2385
2386 @command("qprev", seriesopts, _('hg qprev [-s]'))
2386 @command("qprev", seriesopts, _('hg qprev [-s]'))
2387 def prev(ui, repo, **opts):
2387 def prev(ui, repo, **opts):
2388 """print the name of the preceding applied patch
2388 """print the name of the preceding applied patch
2389
2389
2390 Returns 0 on success."""
2390 Returns 0 on success."""
2391 q = repo.mq
2391 q = repo.mq
2392 l = len(q.applied)
2392 l = len(q.applied)
2393 if l == 1:
2393 if l == 1:
2394 ui.write(_("only one patch applied\n"))
2394 ui.write(_("only one patch applied\n"))
2395 return 1
2395 return 1
2396 if not l:
2396 if not l:
2397 ui.write(_("no patches applied\n"))
2397 ui.write(_("no patches applied\n"))
2398 return 1
2398 return 1
2399 idx = q.series.index(q.applied[-2].name)
2399 idx = q.series.index(q.applied[-2].name)
2400 q.qseries(repo, start=idx, length=1, status='A',
2400 q.qseries(repo, start=idx, length=1, status='A',
2401 summary=opts.get('summary'))
2401 summary=opts.get('summary'))
2402
2402
2403 def setupheaderopts(ui, opts):
2403 def setupheaderopts(ui, opts):
2404 if not opts.get('user') and opts.get('currentuser'):
2404 if not opts.get('user') and opts.get('currentuser'):
2405 opts['user'] = ui.username()
2405 opts['user'] = ui.username()
2406 if not opts.get('date') and opts.get('currentdate'):
2406 if not opts.get('date') and opts.get('currentdate'):
2407 opts['date'] = "%d %d" % util.makedate()
2407 opts['date'] = "%d %d" % util.makedate()
2408
2408
2409 @command("^qnew",
2409 @command("^qnew",
2410 [('e', 'edit', None, _('edit commit message')),
2410 [('e', 'edit', None, _('edit commit message')),
2411 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2411 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2412 ('g', 'git', None, _('use git extended diff format')),
2412 ('g', 'git', None, _('use git extended diff format')),
2413 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2413 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2414 ('u', 'user', '',
2414 ('u', 'user', '',
2415 _('add "From: <USER>" to patch'), _('USER')),
2415 _('add "From: <USER>" to patch'), _('USER')),
2416 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2416 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2417 ('d', 'date', '',
2417 ('d', 'date', '',
2418 _('add "Date: <DATE>" to patch'), _('DATE'))
2418 _('add "Date: <DATE>" to patch'), _('DATE'))
2419 ] + commands.walkopts + commands.commitopts,
2419 ] + commands.walkopts + commands.commitopts,
2420 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'))
2420 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'))
2421 def new(ui, repo, patch, *args, **opts):
2421 def new(ui, repo, patch, *args, **opts):
2422 """create a new patch
2422 """create a new patch
2423
2423
2424 qnew creates a new patch on top of the currently-applied patch (if
2424 qnew creates a new patch on top of the currently-applied patch (if
2425 any). The patch will be initialized with any outstanding changes
2425 any). The patch will be initialized with any outstanding changes
2426 in the working directory. You may also use -I/--include,
2426 in the working directory. You may also use -I/--include,
2427 -X/--exclude, and/or a list of files after the patch name to add
2427 -X/--exclude, and/or a list of files after the patch name to add
2428 only changes to matching files to the new patch, leaving the rest
2428 only changes to matching files to the new patch, leaving the rest
2429 as uncommitted modifications.
2429 as uncommitted modifications.
2430
2430
2431 -u/--user and -d/--date can be used to set the (given) user and
2431 -u/--user and -d/--date can be used to set the (given) user and
2432 date, respectively. -U/--currentuser and -D/--currentdate set user
2432 date, respectively. -U/--currentuser and -D/--currentdate set user
2433 to current user and date to current date.
2433 to current user and date to current date.
2434
2434
2435 -e/--edit, -m/--message or -l/--logfile set the patch header as
2435 -e/--edit, -m/--message or -l/--logfile set the patch header as
2436 well as the commit message. If none is specified, the header is
2436 well as the commit message. If none is specified, the header is
2437 empty and the commit message is '[mq]: PATCH'.
2437 empty and the commit message is '[mq]: PATCH'.
2438
2438
2439 Use the -g/--git option to keep the patch in the git extended diff
2439 Use the -g/--git option to keep the patch in the git extended diff
2440 format. Read the diffs help topic for more information on why this
2440 format. Read the diffs help topic for more information on why this
2441 is important for preserving permission changes and copy/rename
2441 is important for preserving permission changes and copy/rename
2442 information.
2442 information.
2443
2443
2444 Returns 0 on successful creation of a new patch.
2444 Returns 0 on successful creation of a new patch.
2445 """
2445 """
2446 msg = cmdutil.logmessage(ui, opts)
2446 msg = cmdutil.logmessage(ui, opts)
2447 q = repo.mq
2447 q = repo.mq
2448 opts['msg'] = msg
2448 opts['msg'] = msg
2449 setupheaderopts(ui, opts)
2449 setupheaderopts(ui, opts)
2450 q.new(repo, patch, *args, **opts)
2450 q.new(repo, patch, *args, **opts)
2451 q.savedirty()
2451 q.savedirty()
2452 return 0
2452 return 0
2453
2453
2454 @command("^qrefresh",
2454 @command("^qrefresh",
2455 [('e', 'edit', None, _('edit commit message')),
2455 [('e', 'edit', None, _('edit commit message')),
2456 ('g', 'git', None, _('use git extended diff format')),
2456 ('g', 'git', None, _('use git extended diff format')),
2457 ('s', 'short', None,
2457 ('s', 'short', None,
2458 _('refresh only files already in the patch and specified files')),
2458 _('refresh only files already in the patch and specified files')),
2459 ('U', 'currentuser', None,
2459 ('U', 'currentuser', None,
2460 _('add/update author field in patch with current user')),
2460 _('add/update author field in patch with current user')),
2461 ('u', 'user', '',
2461 ('u', 'user', '',
2462 _('add/update author field in patch with given user'), _('USER')),
2462 _('add/update author field in patch with given user'), _('USER')),
2463 ('D', 'currentdate', None,
2463 ('D', 'currentdate', None,
2464 _('add/update date field in patch with current date')),
2464 _('add/update date field in patch with current date')),
2465 ('d', 'date', '',
2465 ('d', 'date', '',
2466 _('add/update date field in patch with given date'), _('DATE'))
2466 _('add/update date field in patch with given date'), _('DATE'))
2467 ] + commands.walkopts + commands.commitopts,
2467 ] + commands.walkopts + commands.commitopts,
2468 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'))
2468 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'))
2469 def refresh(ui, repo, *pats, **opts):
2469 def refresh(ui, repo, *pats, **opts):
2470 """update the current patch
2470 """update the current patch
2471
2471
2472 If any file patterns are provided, the refreshed patch will
2472 If any file patterns are provided, the refreshed patch will
2473 contain only the modifications that match those patterns; the
2473 contain only the modifications that match those patterns; the
2474 remaining modifications will remain in the working directory.
2474 remaining modifications will remain in the working directory.
2475
2475
2476 If -s/--short is specified, files currently included in the patch
2476 If -s/--short is specified, files currently included in the patch
2477 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.
2478
2478
2479 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
2480 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
2481 your message in ``.hg/last-message.txt``.
2481 your message in ``.hg/last-message.txt``.
2482
2482
2483 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
2484 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
2485 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
2486 git diff format.
2486 git diff format.
2487
2487
2488 Returns 0 on success.
2488 Returns 0 on success.
2489 """
2489 """
2490 q = repo.mq
2490 q = repo.mq
2491 message = cmdutil.logmessage(ui, opts)
2491 message = cmdutil.logmessage(ui, opts)
2492 if opts.get('edit'):
2493 if message:
2494 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2495 setupheaderopts(ui, opts)
2492 setupheaderopts(ui, opts)
2496 wlock = repo.wlock()
2493 wlock = repo.wlock()
2497 try:
2494 try:
2498 ret = q.refresh(repo, pats, msg=message, **opts)
2495 ret = q.refresh(repo, pats, msg=message, **opts)
2499 q.savedirty()
2496 q.savedirty()
2500 return ret
2497 return ret
2501 finally:
2498 finally:
2502 wlock.release()
2499 wlock.release()
2503
2500
2504 @command("^qdiff",
2501 @command("^qdiff",
2505 commands.diffopts + commands.diffopts2 + commands.walkopts,
2502 commands.diffopts + commands.diffopts2 + commands.walkopts,
2506 _('hg qdiff [OPTION]... [FILE]...'))
2503 _('hg qdiff [OPTION]... [FILE]...'))
2507 def diff(ui, repo, *pats, **opts):
2504 def diff(ui, repo, *pats, **opts):
2508 """diff of the current patch and subsequent modifications
2505 """diff of the current patch and subsequent modifications
2509
2506
2510 Shows a diff which includes the current patch as well as any
2507 Shows a diff which includes the current patch as well as any
2511 changes which have been made in the working directory since the
2508 changes which have been made in the working directory since the
2512 last refresh (thus showing what the current patch would become
2509 last refresh (thus showing what the current patch would become
2513 after a qrefresh).
2510 after a qrefresh).
2514
2511
2515 Use :hg:`diff` if you only want to see the changes made since the
2512 Use :hg:`diff` if you only want to see the changes made since the
2516 last qrefresh, or :hg:`export qtip` if you want to see changes
2513 last qrefresh, or :hg:`export qtip` if you want to see changes
2517 made by the current patch without including changes made since the
2514 made by the current patch without including changes made since the
2518 qrefresh.
2515 qrefresh.
2519
2516
2520 Returns 0 on success.
2517 Returns 0 on success.
2521 """
2518 """
2522 repo.mq.diff(repo, pats, opts)
2519 repo.mq.diff(repo, pats, opts)
2523 return 0
2520 return 0
2524
2521
2525 @command('qfold',
2522 @command('qfold',
2526 [('e', 'edit', None, _('edit patch header')),
2523 [('e', 'edit', None, _('edit patch header')),
2527 ('k', 'keep', None, _('keep folded patch files')),
2524 ('k', 'keep', None, _('keep folded patch files')),
2528 ] + commands.commitopts,
2525 ] + commands.commitopts,
2529 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2526 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2530 def fold(ui, repo, *files, **opts):
2527 def fold(ui, repo, *files, **opts):
2531 """fold the named patches into the current patch
2528 """fold the named patches into the current patch
2532
2529
2533 Patches must not yet be applied. Each patch will be successively
2530 Patches must not yet be applied. Each patch will be successively
2534 applied to the current patch in the order given. If all the
2531 applied to the current patch in the order given. If all the
2535 patches apply successfully, the current patch will be refreshed
2532 patches apply successfully, the current patch will be refreshed
2536 with the new cumulative patch, and the folded patches will be
2533 with the new cumulative patch, and the folded patches will be
2537 deleted. With -k/--keep, the folded patch files will not be
2534 deleted. With -k/--keep, the folded patch files will not be
2538 removed afterwards.
2535 removed afterwards.
2539
2536
2540 The header for each folded patch will be concatenated with the
2537 The header for each folded patch will be concatenated with the
2541 current patch header, separated by a line of ``* * *``.
2538 current patch header, separated by a line of ``* * *``.
2542
2539
2543 Returns 0 on success."""
2540 Returns 0 on success."""
2544 q = repo.mq
2541 q = repo.mq
2545 if not files:
2542 if not files:
2546 raise util.Abort(_('qfold requires at least one patch name'))
2543 raise util.Abort(_('qfold requires at least one patch name'))
2547 if not q.checktoppatch(repo)[0]:
2544 if not q.checktoppatch(repo)[0]:
2548 raise util.Abort(_('no patches applied'))
2545 raise util.Abort(_('no patches applied'))
2549 q.checklocalchanges(repo)
2546 q.checklocalchanges(repo)
2550
2547
2551 message = cmdutil.logmessage(ui, opts)
2548 message = cmdutil.logmessage(ui, opts)
2552 if opts.get('edit'):
2549 if opts.get('edit'):
2553 if message:
2550 if message:
2554 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2551 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2555
2552
2556 parent = q.lookup('qtip')
2553 parent = q.lookup('qtip')
2557 patches = []
2554 patches = []
2558 messages = []
2555 messages = []
2559 for f in files:
2556 for f in files:
2560 p = q.lookup(f)
2557 p = q.lookup(f)
2561 if p in patches or p == parent:
2558 if p in patches or p == parent:
2562 ui.warn(_('skipping already folded patch %s\n') % p)
2559 ui.warn(_('skipping already folded patch %s\n') % p)
2563 if q.isapplied(p):
2560 if q.isapplied(p):
2564 raise util.Abort(_('qfold cannot fold already applied patch %s')
2561 raise util.Abort(_('qfold cannot fold already applied patch %s')
2565 % p)
2562 % p)
2566 patches.append(p)
2563 patches.append(p)
2567
2564
2568 for p in patches:
2565 for p in patches:
2569 if not message:
2566 if not message:
2570 ph = patchheader(q.join(p), q.plainmode)
2567 ph = patchheader(q.join(p), q.plainmode)
2571 if ph.message:
2568 if ph.message:
2572 messages.append(ph.message)
2569 messages.append(ph.message)
2573 pf = q.join(p)
2570 pf = q.join(p)
2574 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2571 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2575 if not patchsuccess:
2572 if not patchsuccess:
2576 raise util.Abort(_('error folding patch %s') % p)
2573 raise util.Abort(_('error folding patch %s') % p)
2577
2574
2578 if not message:
2575 if not message:
2579 ph = patchheader(q.join(parent), q.plainmode)
2576 ph = patchheader(q.join(parent), q.plainmode)
2580 message = ph.message
2577 message = ph.message
2581 for msg in messages:
2578 for msg in messages:
2582 if msg:
2579 if msg:
2583 if message:
2580 if message:
2584 message.append('* * *')
2581 message.append('* * *')
2585 message.extend(msg)
2582 message.extend(msg)
2586 message = '\n'.join(message)
2583 message = '\n'.join(message)
2587
2584
2588 diffopts = q.patchopts(q.diffopts(), *patches)
2585 diffopts = q.patchopts(q.diffopts(), *patches)
2589 wlock = repo.wlock()
2586 wlock = repo.wlock()
2590 try:
2587 try:
2591 q.refresh(repo, msg=message, git=diffopts.git, edit=opts.get('edit'))
2588 q.refresh(repo, msg=message, git=diffopts.git, edit=opts.get('edit'))
2592 q.delete(repo, patches, opts)
2589 q.delete(repo, patches, opts)
2593 q.savedirty()
2590 q.savedirty()
2594 finally:
2591 finally:
2595 wlock.release()
2592 wlock.release()
2596
2593
2597 @command("qgoto",
2594 @command("qgoto",
2598 [('', 'keep-changes', None,
2595 [('', 'keep-changes', None,
2599 _('tolerate non-conflicting local changes')),
2596 _('tolerate non-conflicting local changes')),
2600 ('f', 'force', None, _('overwrite any local changes')),
2597 ('f', 'force', None, _('overwrite any local changes')),
2601 ('', 'no-backup', None, _('do not save backup copies of files'))],
2598 ('', 'no-backup', None, _('do not save backup copies of files'))],
2602 _('hg qgoto [OPTION]... PATCH'))
2599 _('hg qgoto [OPTION]... PATCH'))
2603 def goto(ui, repo, patch, **opts):
2600 def goto(ui, repo, patch, **opts):
2604 '''push or pop patches until named patch is at top of stack
2601 '''push or pop patches until named patch is at top of stack
2605
2602
2606 Returns 0 on success.'''
2603 Returns 0 on success.'''
2607 opts = fixkeepchangesopts(ui, opts)
2604 opts = fixkeepchangesopts(ui, opts)
2608 q = repo.mq
2605 q = repo.mq
2609 patch = q.lookup(patch)
2606 patch = q.lookup(patch)
2610 nobackup = opts.get('no_backup')
2607 nobackup = opts.get('no_backup')
2611 keepchanges = opts.get('keep_changes')
2608 keepchanges = opts.get('keep_changes')
2612 if q.isapplied(patch):
2609 if q.isapplied(patch):
2613 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2610 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2614 keepchanges=keepchanges)
2611 keepchanges=keepchanges)
2615 else:
2612 else:
2616 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2613 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2617 keepchanges=keepchanges)
2614 keepchanges=keepchanges)
2618 q.savedirty()
2615 q.savedirty()
2619 return ret
2616 return ret
2620
2617
2621 @command("qguard",
2618 @command("qguard",
2622 [('l', 'list', None, _('list all patches and guards')),
2619 [('l', 'list', None, _('list all patches and guards')),
2623 ('n', 'none', None, _('drop all guards'))],
2620 ('n', 'none', None, _('drop all guards'))],
2624 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2621 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2625 def guard(ui, repo, *args, **opts):
2622 def guard(ui, repo, *args, **opts):
2626 '''set or print guards for a patch
2623 '''set or print guards for a patch
2627
2624
2628 Guards control whether a patch can be pushed. A patch with no
2625 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
2626 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
2627 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
2628 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2632 has activated it.
2629 has activated it.
2633
2630
2634 With no arguments, print the currently active guards.
2631 With no arguments, print the currently active guards.
2635 With arguments, set guards for the named patch.
2632 With arguments, set guards for the named patch.
2636
2633
2637 .. note::
2634 .. note::
2638
2635
2639 Specifying negative guards now requires '--'.
2636 Specifying negative guards now requires '--'.
2640
2637
2641 To set guards on another patch::
2638 To set guards on another patch::
2642
2639
2643 hg qguard other.patch -- +2.6.17 -stable
2640 hg qguard other.patch -- +2.6.17 -stable
2644
2641
2645 Returns 0 on success.
2642 Returns 0 on success.
2646 '''
2643 '''
2647 def status(idx):
2644 def status(idx):
2648 guards = q.seriesguards[idx] or ['unguarded']
2645 guards = q.seriesguards[idx] or ['unguarded']
2649 if q.series[idx] in applied:
2646 if q.series[idx] in applied:
2650 state = 'applied'
2647 state = 'applied'
2651 elif q.pushable(idx)[0]:
2648 elif q.pushable(idx)[0]:
2652 state = 'unapplied'
2649 state = 'unapplied'
2653 else:
2650 else:
2654 state = 'guarded'
2651 state = 'guarded'
2655 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2652 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2656 ui.write('%s: ' % ui.label(q.series[idx], label))
2653 ui.write('%s: ' % ui.label(q.series[idx], label))
2657
2654
2658 for i, guard in enumerate(guards):
2655 for i, guard in enumerate(guards):
2659 if guard.startswith('+'):
2656 if guard.startswith('+'):
2660 ui.write(guard, label='qguard.positive')
2657 ui.write(guard, label='qguard.positive')
2661 elif guard.startswith('-'):
2658 elif guard.startswith('-'):
2662 ui.write(guard, label='qguard.negative')
2659 ui.write(guard, label='qguard.negative')
2663 else:
2660 else:
2664 ui.write(guard, label='qguard.unguarded')
2661 ui.write(guard, label='qguard.unguarded')
2665 if i != len(guards) - 1:
2662 if i != len(guards) - 1:
2666 ui.write(' ')
2663 ui.write(' ')
2667 ui.write('\n')
2664 ui.write('\n')
2668 q = repo.mq
2665 q = repo.mq
2669 applied = set(p.name for p in q.applied)
2666 applied = set(p.name for p in q.applied)
2670 patch = None
2667 patch = None
2671 args = list(args)
2668 args = list(args)
2672 if opts.get('list'):
2669 if opts.get('list'):
2673 if args or opts.get('none'):
2670 if args or opts.get('none'):
2674 raise util.Abort(_('cannot mix -l/--list with options or '
2671 raise util.Abort(_('cannot mix -l/--list with options or '
2675 'arguments'))
2672 'arguments'))
2676 for i in xrange(len(q.series)):
2673 for i in xrange(len(q.series)):
2677 status(i)
2674 status(i)
2678 return
2675 return
2679 if not args or args[0][0:1] in '-+':
2676 if not args or args[0][0:1] in '-+':
2680 if not q.applied:
2677 if not q.applied:
2681 raise util.Abort(_('no patches applied'))
2678 raise util.Abort(_('no patches applied'))
2682 patch = q.applied[-1].name
2679 patch = q.applied[-1].name
2683 if patch is None and args[0][0:1] not in '-+':
2680 if patch is None and args[0][0:1] not in '-+':
2684 patch = args.pop(0)
2681 patch = args.pop(0)
2685 if patch is None:
2682 if patch is None:
2686 raise util.Abort(_('no patch to work with'))
2683 raise util.Abort(_('no patch to work with'))
2687 if args or opts.get('none'):
2684 if args or opts.get('none'):
2688 idx = q.findseries(patch)
2685 idx = q.findseries(patch)
2689 if idx is None:
2686 if idx is None:
2690 raise util.Abort(_('no patch named %s') % patch)
2687 raise util.Abort(_('no patch named %s') % patch)
2691 q.setguards(idx, args)
2688 q.setguards(idx, args)
2692 q.savedirty()
2689 q.savedirty()
2693 else:
2690 else:
2694 status(q.series.index(q.lookup(patch)))
2691 status(q.series.index(q.lookup(patch)))
2695
2692
2696 @command("qheader", [], _('hg qheader [PATCH]'))
2693 @command("qheader", [], _('hg qheader [PATCH]'))
2697 def header(ui, repo, patch=None):
2694 def header(ui, repo, patch=None):
2698 """print the header of the topmost or specified patch
2695 """print the header of the topmost or specified patch
2699
2696
2700 Returns 0 on success."""
2697 Returns 0 on success."""
2701 q = repo.mq
2698 q = repo.mq
2702
2699
2703 if patch:
2700 if patch:
2704 patch = q.lookup(patch)
2701 patch = q.lookup(patch)
2705 else:
2702 else:
2706 if not q.applied:
2703 if not q.applied:
2707 ui.write(_('no patches applied\n'))
2704 ui.write(_('no patches applied\n'))
2708 return 1
2705 return 1
2709 patch = q.lookup('qtip')
2706 patch = q.lookup('qtip')
2710 ph = patchheader(q.join(patch), q.plainmode)
2707 ph = patchheader(q.join(patch), q.plainmode)
2711
2708
2712 ui.write('\n'.join(ph.message) + '\n')
2709 ui.write('\n'.join(ph.message) + '\n')
2713
2710
2714 def lastsavename(path):
2711 def lastsavename(path):
2715 (directory, base) = os.path.split(path)
2712 (directory, base) = os.path.split(path)
2716 names = os.listdir(directory)
2713 names = os.listdir(directory)
2717 namere = re.compile("%s.([0-9]+)" % base)
2714 namere = re.compile("%s.([0-9]+)" % base)
2718 maxindex = None
2715 maxindex = None
2719 maxname = None
2716 maxname = None
2720 for f in names:
2717 for f in names:
2721 m = namere.match(f)
2718 m = namere.match(f)
2722 if m:
2719 if m:
2723 index = int(m.group(1))
2720 index = int(m.group(1))
2724 if maxindex is None or index > maxindex:
2721 if maxindex is None or index > maxindex:
2725 maxindex = index
2722 maxindex = index
2726 maxname = f
2723 maxname = f
2727 if maxname:
2724 if maxname:
2728 return (os.path.join(directory, maxname), maxindex)
2725 return (os.path.join(directory, maxname), maxindex)
2729 return (None, None)
2726 return (None, None)
2730
2727
2731 def savename(path):
2728 def savename(path):
2732 (last, index) = lastsavename(path)
2729 (last, index) = lastsavename(path)
2733 if last is None:
2730 if last is None:
2734 index = 0
2731 index = 0
2735 newpath = path + ".%d" % (index + 1)
2732 newpath = path + ".%d" % (index + 1)
2736 return newpath
2733 return newpath
2737
2734
2738 @command("^qpush",
2735 @command("^qpush",
2739 [('', 'keep-changes', None,
2736 [('', 'keep-changes', None,
2740 _('tolerate non-conflicting local changes')),
2737 _('tolerate non-conflicting local changes')),
2741 ('f', 'force', None, _('apply on top of local changes')),
2738 ('f', 'force', None, _('apply on top of local changes')),
2742 ('e', 'exact', None,
2739 ('e', 'exact', None,
2743 _('apply the target patch to its recorded parent')),
2740 _('apply the target patch to its recorded parent')),
2744 ('l', 'list', None, _('list patch name in commit text')),
2741 ('l', 'list', None, _('list patch name in commit text')),
2745 ('a', 'all', None, _('apply all patches')),
2742 ('a', 'all', None, _('apply all patches')),
2746 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2743 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2747 ('n', 'name', '',
2744 ('n', 'name', '',
2748 _('merge queue name (DEPRECATED)'), _('NAME')),
2745 _('merge queue name (DEPRECATED)'), _('NAME')),
2749 ('', 'move', None,
2746 ('', 'move', None,
2750 _('reorder patch series and apply only the patch')),
2747 _('reorder patch series and apply only the patch')),
2751 ('', 'no-backup', None, _('do not save backup copies of files'))],
2748 ('', 'no-backup', None, _('do not save backup copies of files'))],
2752 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2749 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2753 def push(ui, repo, patch=None, **opts):
2750 def push(ui, repo, patch=None, **opts):
2754 """push the next patch onto the stack
2751 """push the next patch onto the stack
2755
2752
2756 By default, abort if the working directory contains uncommitted
2753 By default, abort if the working directory contains uncommitted
2757 changes. With --keep-changes, abort only if the uncommitted files
2754 changes. With --keep-changes, abort only if the uncommitted files
2758 overlap with patched files. With -f/--force, backup and patch over
2755 overlap with patched files. With -f/--force, backup and patch over
2759 uncommitted changes.
2756 uncommitted changes.
2760
2757
2761 Return 0 on success.
2758 Return 0 on success.
2762 """
2759 """
2763 q = repo.mq
2760 q = repo.mq
2764 mergeq = None
2761 mergeq = None
2765
2762
2766 opts = fixkeepchangesopts(ui, opts)
2763 opts = fixkeepchangesopts(ui, opts)
2767 if opts.get('merge'):
2764 if opts.get('merge'):
2768 if opts.get('name'):
2765 if opts.get('name'):
2769 newpath = repo.join(opts.get('name'))
2766 newpath = repo.join(opts.get('name'))
2770 else:
2767 else:
2771 newpath, i = lastsavename(q.path)
2768 newpath, i = lastsavename(q.path)
2772 if not newpath:
2769 if not newpath:
2773 ui.warn(_("no saved queues found, please use -n\n"))
2770 ui.warn(_("no saved queues found, please use -n\n"))
2774 return 1
2771 return 1
2775 mergeq = queue(ui, repo.baseui, repo.path, newpath)
2772 mergeq = queue(ui, repo.baseui, repo.path, newpath)
2776 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2773 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2777 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2774 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2778 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2775 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2779 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2776 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2780 keepchanges=opts.get('keep_changes'))
2777 keepchanges=opts.get('keep_changes'))
2781 return ret
2778 return ret
2782
2779
2783 @command("^qpop",
2780 @command("^qpop",
2784 [('a', 'all', None, _('pop all patches')),
2781 [('a', 'all', None, _('pop all patches')),
2785 ('n', 'name', '',
2782 ('n', 'name', '',
2786 _('queue name to pop (DEPRECATED)'), _('NAME')),
2783 _('queue name to pop (DEPRECATED)'), _('NAME')),
2787 ('', 'keep-changes', None,
2784 ('', 'keep-changes', None,
2788 _('tolerate non-conflicting local changes')),
2785 _('tolerate non-conflicting local changes')),
2789 ('f', 'force', None, _('forget any local changes to patched files')),
2786 ('f', 'force', None, _('forget any local changes to patched files')),
2790 ('', 'no-backup', None, _('do not save backup copies of files'))],
2787 ('', 'no-backup', None, _('do not save backup copies of files'))],
2791 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2788 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2792 def pop(ui, repo, patch=None, **opts):
2789 def pop(ui, repo, patch=None, **opts):
2793 """pop the current patch off the stack
2790 """pop the current patch off the stack
2794
2791
2795 Without argument, pops off the top of the patch stack. If given a
2792 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
2793 patch name, keeps popping off patches until the named patch is at
2797 the top of the stack.
2794 the top of the stack.
2798
2795
2799 By default, abort if the working directory contains uncommitted
2796 By default, abort if the working directory contains uncommitted
2800 changes. With --keep-changes, abort only if the uncommitted files
2797 changes. With --keep-changes, abort only if the uncommitted files
2801 overlap with patched files. With -f/--force, backup and discard
2798 overlap with patched files. With -f/--force, backup and discard
2802 changes made to such files.
2799 changes made to such files.
2803
2800
2804 Return 0 on success.
2801 Return 0 on success.
2805 """
2802 """
2806 opts = fixkeepchangesopts(ui, opts)
2803 opts = fixkeepchangesopts(ui, opts)
2807 localupdate = True
2804 localupdate = True
2808 if opts.get('name'):
2805 if opts.get('name'):
2809 q = queue(ui, repo.baseui, repo.path, repo.join(opts.get('name')))
2806 q = queue(ui, repo.baseui, repo.path, repo.join(opts.get('name')))
2810 ui.warn(_('using patch queue: %s\n') % q.path)
2807 ui.warn(_('using patch queue: %s\n') % q.path)
2811 localupdate = False
2808 localupdate = False
2812 else:
2809 else:
2813 q = repo.mq
2810 q = repo.mq
2814 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2811 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2815 all=opts.get('all'), nobackup=opts.get('no_backup'),
2812 all=opts.get('all'), nobackup=opts.get('no_backup'),
2816 keepchanges=opts.get('keep_changes'))
2813 keepchanges=opts.get('keep_changes'))
2817 q.savedirty()
2814 q.savedirty()
2818 return ret
2815 return ret
2819
2816
2820 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2817 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2821 def rename(ui, repo, patch, name=None, **opts):
2818 def rename(ui, repo, patch, name=None, **opts):
2822 """rename a patch
2819 """rename a patch
2823
2820
2824 With one argument, renames the current patch to PATCH1.
2821 With one argument, renames the current patch to PATCH1.
2825 With two arguments, renames PATCH1 to PATCH2.
2822 With two arguments, renames PATCH1 to PATCH2.
2826
2823
2827 Returns 0 on success."""
2824 Returns 0 on success."""
2828 q = repo.mq
2825 q = repo.mq
2829 if not name:
2826 if not name:
2830 name = patch
2827 name = patch
2831 patch = None
2828 patch = None
2832
2829
2833 if patch:
2830 if patch:
2834 patch = q.lookup(patch)
2831 patch = q.lookup(patch)
2835 else:
2832 else:
2836 if not q.applied:
2833 if not q.applied:
2837 ui.write(_('no patches applied\n'))
2834 ui.write(_('no patches applied\n'))
2838 return
2835 return
2839 patch = q.lookup('qtip')
2836 patch = q.lookup('qtip')
2840 absdest = q.join(name)
2837 absdest = q.join(name)
2841 if os.path.isdir(absdest):
2838 if os.path.isdir(absdest):
2842 name = normname(os.path.join(name, os.path.basename(patch)))
2839 name = normname(os.path.join(name, os.path.basename(patch)))
2843 absdest = q.join(name)
2840 absdest = q.join(name)
2844 q.checkpatchname(name)
2841 q.checkpatchname(name)
2845
2842
2846 ui.note(_('renaming %s to %s\n') % (patch, name))
2843 ui.note(_('renaming %s to %s\n') % (patch, name))
2847 i = q.findseries(patch)
2844 i = q.findseries(patch)
2848 guards = q.guard_re.findall(q.fullseries[i])
2845 guards = q.guard_re.findall(q.fullseries[i])
2849 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2846 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2850 q.parseseries()
2847 q.parseseries()
2851 q.seriesdirty = True
2848 q.seriesdirty = True
2852
2849
2853 info = q.isapplied(patch)
2850 info = q.isapplied(patch)
2854 if info:
2851 if info:
2855 q.applied[info[0]] = statusentry(info[1], name)
2852 q.applied[info[0]] = statusentry(info[1], name)
2856 q.applieddirty = True
2853 q.applieddirty = True
2857
2854
2858 destdir = os.path.dirname(absdest)
2855 destdir = os.path.dirname(absdest)
2859 if not os.path.isdir(destdir):
2856 if not os.path.isdir(destdir):
2860 os.makedirs(destdir)
2857 os.makedirs(destdir)
2861 util.rename(q.join(patch), absdest)
2858 util.rename(q.join(patch), absdest)
2862 r = q.qrepo()
2859 r = q.qrepo()
2863 if r and patch in r.dirstate:
2860 if r and patch in r.dirstate:
2864 wctx = r[None]
2861 wctx = r[None]
2865 wlock = r.wlock()
2862 wlock = r.wlock()
2866 try:
2863 try:
2867 if r.dirstate[patch] == 'a':
2864 if r.dirstate[patch] == 'a':
2868 r.dirstate.drop(patch)
2865 r.dirstate.drop(patch)
2869 r.dirstate.add(name)
2866 r.dirstate.add(name)
2870 else:
2867 else:
2871 wctx.copy(patch, name)
2868 wctx.copy(patch, name)
2872 wctx.forget([patch])
2869 wctx.forget([patch])
2873 finally:
2870 finally:
2874 wlock.release()
2871 wlock.release()
2875
2872
2876 q.savedirty()
2873 q.savedirty()
2877
2874
2878 @command("qrestore",
2875 @command("qrestore",
2879 [('d', 'delete', None, _('delete save entry')),
2876 [('d', 'delete', None, _('delete save entry')),
2880 ('u', 'update', None, _('update queue working directory'))],
2877 ('u', 'update', None, _('update queue working directory'))],
2881 _('hg qrestore [-d] [-u] REV'))
2878 _('hg qrestore [-d] [-u] REV'))
2882 def restore(ui, repo, rev, **opts):
2879 def restore(ui, repo, rev, **opts):
2883 """restore the queue state saved by a revision (DEPRECATED)
2880 """restore the queue state saved by a revision (DEPRECATED)
2884
2881
2885 This command is deprecated, use :hg:`rebase` instead."""
2882 This command is deprecated, use :hg:`rebase` instead."""
2886 rev = repo.lookup(rev)
2883 rev = repo.lookup(rev)
2887 q = repo.mq
2884 q = repo.mq
2888 q.restore(repo, rev, delete=opts.get('delete'),
2885 q.restore(repo, rev, delete=opts.get('delete'),
2889 qupdate=opts.get('update'))
2886 qupdate=opts.get('update'))
2890 q.savedirty()
2887 q.savedirty()
2891 return 0
2888 return 0
2892
2889
2893 @command("qsave",
2890 @command("qsave",
2894 [('c', 'copy', None, _('copy patch directory')),
2891 [('c', 'copy', None, _('copy patch directory')),
2895 ('n', 'name', '',
2892 ('n', 'name', '',
2896 _('copy directory name'), _('NAME')),
2893 _('copy directory name'), _('NAME')),
2897 ('e', 'empty', None, _('clear queue status file')),
2894 ('e', 'empty', None, _('clear queue status file')),
2898 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2895 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2899 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
2896 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
2900 def save(ui, repo, **opts):
2897 def save(ui, repo, **opts):
2901 """save current queue state (DEPRECATED)
2898 """save current queue state (DEPRECATED)
2902
2899
2903 This command is deprecated, use :hg:`rebase` instead."""
2900 This command is deprecated, use :hg:`rebase` instead."""
2904 q = repo.mq
2901 q = repo.mq
2905 message = cmdutil.logmessage(ui, opts)
2902 message = cmdutil.logmessage(ui, opts)
2906 ret = q.save(repo, msg=message)
2903 ret = q.save(repo, msg=message)
2907 if ret:
2904 if ret:
2908 return ret
2905 return ret
2909 q.savedirty() # save to .hg/patches before copying
2906 q.savedirty() # save to .hg/patches before copying
2910 if opts.get('copy'):
2907 if opts.get('copy'):
2911 path = q.path
2908 path = q.path
2912 if opts.get('name'):
2909 if opts.get('name'):
2913 newpath = os.path.join(q.basepath, opts.get('name'))
2910 newpath = os.path.join(q.basepath, opts.get('name'))
2914 if os.path.exists(newpath):
2911 if os.path.exists(newpath):
2915 if not os.path.isdir(newpath):
2912 if not os.path.isdir(newpath):
2916 raise util.Abort(_('destination %s exists and is not '
2913 raise util.Abort(_('destination %s exists and is not '
2917 'a directory') % newpath)
2914 'a directory') % newpath)
2918 if not opts.get('force'):
2915 if not opts.get('force'):
2919 raise util.Abort(_('destination %s exists, '
2916 raise util.Abort(_('destination %s exists, '
2920 'use -f to force') % newpath)
2917 'use -f to force') % newpath)
2921 else:
2918 else:
2922 newpath = savename(path)
2919 newpath = savename(path)
2923 ui.warn(_("copy %s to %s\n") % (path, newpath))
2920 ui.warn(_("copy %s to %s\n") % (path, newpath))
2924 util.copyfiles(path, newpath)
2921 util.copyfiles(path, newpath)
2925 if opts.get('empty'):
2922 if opts.get('empty'):
2926 del q.applied[:]
2923 del q.applied[:]
2927 q.applieddirty = True
2924 q.applieddirty = True
2928 q.savedirty()
2925 q.savedirty()
2929 return 0
2926 return 0
2930
2927
2931
2928
2932 @command("qselect",
2929 @command("qselect",
2933 [('n', 'none', None, _('disable all guards')),
2930 [('n', 'none', None, _('disable all guards')),
2934 ('s', 'series', None, _('list all guards in series file')),
2931 ('s', 'series', None, _('list all guards in series file')),
2935 ('', 'pop', None, _('pop to before first guarded applied patch')),
2932 ('', 'pop', None, _('pop to before first guarded applied patch')),
2936 ('', 'reapply', None, _('pop, then reapply patches'))],
2933 ('', 'reapply', None, _('pop, then reapply patches'))],
2937 _('hg qselect [OPTION]... [GUARD]...'))
2934 _('hg qselect [OPTION]... [GUARD]...'))
2938 def select(ui, repo, *args, **opts):
2935 def select(ui, repo, *args, **opts):
2939 '''set or print guarded patches to push
2936 '''set or print guarded patches to push
2940
2937
2941 Use the :hg:`qguard` command to set or print guards on patch, then use
2938 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
2939 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
2940 it has no guards or any positive guards match the currently
2944 selected guard, but will not be pushed if any negative guards
2941 selected guard, but will not be pushed if any negative guards
2945 match the current guard. For example::
2942 match the current guard. For example::
2946
2943
2947 qguard foo.patch -- -stable (negative guard)
2944 qguard foo.patch -- -stable (negative guard)
2948 qguard bar.patch +stable (positive guard)
2945 qguard bar.patch +stable (positive guard)
2949 qselect stable
2946 qselect stable
2950
2947
2951 This activates the "stable" guard. mq will skip foo.patch (because
2948 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
2949 it has a negative match) but push bar.patch (because it has a
2953 positive match).
2950 positive match).
2954
2951
2955 With no arguments, prints the currently active guards.
2952 With no arguments, prints the currently active guards.
2956 With one argument, sets the active guard.
2953 With one argument, sets the active guard.
2957
2954
2958 Use -n/--none to deactivate guards (no other arguments needed).
2955 Use -n/--none to deactivate guards (no other arguments needed).
2959 When no guards are active, patches with positive guards are
2956 When no guards are active, patches with positive guards are
2960 skipped and patches with negative guards are pushed.
2957 skipped and patches with negative guards are pushed.
2961
2958
2962 qselect can change the guards on applied patches. It does not pop
2959 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
2960 guarded patches by default. Use --pop to pop back to the last
2964 applied patch that is not guarded. Use --reapply (which implies
2961 applied patch that is not guarded. Use --reapply (which implies
2965 --pop) to push back to the current patch afterwards, but skip
2962 --pop) to push back to the current patch afterwards, but skip
2966 guarded patches.
2963 guarded patches.
2967
2964
2968 Use -s/--series to print a list of all guards in the series file
2965 Use -s/--series to print a list of all guards in the series file
2969 (no other arguments needed). Use -v for more information.
2966 (no other arguments needed). Use -v for more information.
2970
2967
2971 Returns 0 on success.'''
2968 Returns 0 on success.'''
2972
2969
2973 q = repo.mq
2970 q = repo.mq
2974 guards = q.active()
2971 guards = q.active()
2975 if args or opts.get('none'):
2972 if args or opts.get('none'):
2976 old_unapplied = q.unapplied(repo)
2973 old_unapplied = q.unapplied(repo)
2977 old_guarded = [i for i in xrange(len(q.applied)) if
2974 old_guarded = [i for i in xrange(len(q.applied)) if
2978 not q.pushable(i)[0]]
2975 not q.pushable(i)[0]]
2979 q.setactive(args)
2976 q.setactive(args)
2980 q.savedirty()
2977 q.savedirty()
2981 if not args:
2978 if not args:
2982 ui.status(_('guards deactivated\n'))
2979 ui.status(_('guards deactivated\n'))
2983 if not opts.get('pop') and not opts.get('reapply'):
2980 if not opts.get('pop') and not opts.get('reapply'):
2984 unapplied = q.unapplied(repo)
2981 unapplied = q.unapplied(repo)
2985 guarded = [i for i in xrange(len(q.applied))
2982 guarded = [i for i in xrange(len(q.applied))
2986 if not q.pushable(i)[0]]
2983 if not q.pushable(i)[0]]
2987 if len(unapplied) != len(old_unapplied):
2984 if len(unapplied) != len(old_unapplied):
2988 ui.status(_('number of unguarded, unapplied patches has '
2985 ui.status(_('number of unguarded, unapplied patches has '
2989 'changed from %d to %d\n') %
2986 'changed from %d to %d\n') %
2990 (len(old_unapplied), len(unapplied)))
2987 (len(old_unapplied), len(unapplied)))
2991 if len(guarded) != len(old_guarded):
2988 if len(guarded) != len(old_guarded):
2992 ui.status(_('number of guarded, applied patches has changed '
2989 ui.status(_('number of guarded, applied patches has changed '
2993 'from %d to %d\n') %
2990 'from %d to %d\n') %
2994 (len(old_guarded), len(guarded)))
2991 (len(old_guarded), len(guarded)))
2995 elif opts.get('series'):
2992 elif opts.get('series'):
2996 guards = {}
2993 guards = {}
2997 noguards = 0
2994 noguards = 0
2998 for gs in q.seriesguards:
2995 for gs in q.seriesguards:
2999 if not gs:
2996 if not gs:
3000 noguards += 1
2997 noguards += 1
3001 for g in gs:
2998 for g in gs:
3002 guards.setdefault(g, 0)
2999 guards.setdefault(g, 0)
3003 guards[g] += 1
3000 guards[g] += 1
3004 if ui.verbose:
3001 if ui.verbose:
3005 guards['NONE'] = noguards
3002 guards['NONE'] = noguards
3006 guards = guards.items()
3003 guards = guards.items()
3007 guards.sort(key=lambda x: x[0][1:])
3004 guards.sort(key=lambda x: x[0][1:])
3008 if guards:
3005 if guards:
3009 ui.note(_('guards in series file:\n'))
3006 ui.note(_('guards in series file:\n'))
3010 for guard, count in guards:
3007 for guard, count in guards:
3011 ui.note('%2d ' % count)
3008 ui.note('%2d ' % count)
3012 ui.write(guard, '\n')
3009 ui.write(guard, '\n')
3013 else:
3010 else:
3014 ui.note(_('no guards in series file\n'))
3011 ui.note(_('no guards in series file\n'))
3015 else:
3012 else:
3016 if guards:
3013 if guards:
3017 ui.note(_('active guards:\n'))
3014 ui.note(_('active guards:\n'))
3018 for g in guards:
3015 for g in guards:
3019 ui.write(g, '\n')
3016 ui.write(g, '\n')
3020 else:
3017 else:
3021 ui.write(_('no active guards\n'))
3018 ui.write(_('no active guards\n'))
3022 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
3019 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
3023 popped = False
3020 popped = False
3024 if opts.get('pop') or opts.get('reapply'):
3021 if opts.get('pop') or opts.get('reapply'):
3025 for i in xrange(len(q.applied)):
3022 for i in xrange(len(q.applied)):
3026 pushable, reason = q.pushable(i)
3023 pushable, reason = q.pushable(i)
3027 if not pushable:
3024 if not pushable:
3028 ui.status(_('popping guarded patches\n'))
3025 ui.status(_('popping guarded patches\n'))
3029 popped = True
3026 popped = True
3030 if i == 0:
3027 if i == 0:
3031 q.pop(repo, all=True)
3028 q.pop(repo, all=True)
3032 else:
3029 else:
3033 q.pop(repo, str(i - 1))
3030 q.pop(repo, str(i - 1))
3034 break
3031 break
3035 if popped:
3032 if popped:
3036 try:
3033 try:
3037 if reapply:
3034 if reapply:
3038 ui.status(_('reapplying unguarded patches\n'))
3035 ui.status(_('reapplying unguarded patches\n'))
3039 q.push(repo, reapply)
3036 q.push(repo, reapply)
3040 finally:
3037 finally:
3041 q.savedirty()
3038 q.savedirty()
3042
3039
3043 @command("qfinish",
3040 @command("qfinish",
3044 [('a', 'applied', None, _('finish all applied changesets'))],
3041 [('a', 'applied', None, _('finish all applied changesets'))],
3045 _('hg qfinish [-a] [REV]...'))
3042 _('hg qfinish [-a] [REV]...'))
3046 def finish(ui, repo, *revrange, **opts):
3043 def finish(ui, repo, *revrange, **opts):
3047 """move applied patches into repository history
3044 """move applied patches into repository history
3048
3045
3049 Finishes the specified revisions (corresponding to applied
3046 Finishes the specified revisions (corresponding to applied
3050 patches) by moving them out of mq control into regular repository
3047 patches) by moving them out of mq control into regular repository
3051 history.
3048 history.
3052
3049
3053 Accepts a revision range or the -a/--applied option. If --applied
3050 Accepts a revision range or the -a/--applied option. If --applied
3054 is specified, all applied mq revisions are removed from mq
3051 is specified, all applied mq revisions are removed from mq
3055 control. Otherwise, the given revisions must be at the base of the
3052 control. Otherwise, the given revisions must be at the base of the
3056 stack of applied patches.
3053 stack of applied patches.
3057
3054
3058 This can be especially useful if your changes have been applied to
3055 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
3056 an upstream repository, or if you are about to push your changes
3060 to upstream.
3057 to upstream.
3061
3058
3062 Returns 0 on success.
3059 Returns 0 on success.
3063 """
3060 """
3064 if not opts.get('applied') and not revrange:
3061 if not opts.get('applied') and not revrange:
3065 raise util.Abort(_('no revisions specified'))
3062 raise util.Abort(_('no revisions specified'))
3066 elif opts.get('applied'):
3063 elif opts.get('applied'):
3067 revrange = ('qbase::qtip',) + revrange
3064 revrange = ('qbase::qtip',) + revrange
3068
3065
3069 q = repo.mq
3066 q = repo.mq
3070 if not q.applied:
3067 if not q.applied:
3071 ui.status(_('no patches applied\n'))
3068 ui.status(_('no patches applied\n'))
3072 return 0
3069 return 0
3073
3070
3074 revs = scmutil.revrange(repo, revrange)
3071 revs = scmutil.revrange(repo, revrange)
3075 if repo['.'].rev() in revs and repo[None].files():
3072 if repo['.'].rev() in revs and repo[None].files():
3076 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3073 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3077 # queue.finish may changes phases but leave the responsibility to lock the
3074 # 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
3075 # repo to the caller to avoid deadlock with wlock. This command code is
3079 # responsibility for this locking.
3076 # responsibility for this locking.
3080 lock = repo.lock()
3077 lock = repo.lock()
3081 try:
3078 try:
3082 q.finish(repo, revs)
3079 q.finish(repo, revs)
3083 q.savedirty()
3080 q.savedirty()
3084 finally:
3081 finally:
3085 lock.release()
3082 lock.release()
3086 return 0
3083 return 0
3087
3084
3088 @command("qqueue",
3085 @command("qqueue",
3089 [('l', 'list', False, _('list all available queues')),
3086 [('l', 'list', False, _('list all available queues')),
3090 ('', 'active', False, _('print name of active queue')),
3087 ('', 'active', False, _('print name of active queue')),
3091 ('c', 'create', False, _('create new queue')),
3088 ('c', 'create', False, _('create new queue')),
3092 ('', 'rename', False, _('rename active queue')),
3089 ('', 'rename', False, _('rename active queue')),
3093 ('', 'delete', False, _('delete reference to queue')),
3090 ('', 'delete', False, _('delete reference to queue')),
3094 ('', 'purge', False, _('delete queue, and remove patch dir')),
3091 ('', 'purge', False, _('delete queue, and remove patch dir')),
3095 ],
3092 ],
3096 _('[OPTION] [QUEUE]'))
3093 _('[OPTION] [QUEUE]'))
3097 def qqueue(ui, repo, name=None, **opts):
3094 def qqueue(ui, repo, name=None, **opts):
3098 '''manage multiple patch queues
3095 '''manage multiple patch queues
3099
3096
3100 Supports switching between different patch queues, as well as creating
3097 Supports switching between different patch queues, as well as creating
3101 new patch queues and deleting existing ones.
3098 new patch queues and deleting existing ones.
3102
3099
3103 Omitting a queue name or specifying -l/--list will show you the registered
3100 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
3101 queues - by default the "normal" patches queue is registered. The currently
3105 active queue will be marked with "(active)". Specifying --active will print
3102 active queue will be marked with "(active)". Specifying --active will print
3106 only the name of the active queue.
3103 only the name of the active queue.
3107
3104
3108 To create a new queue, use -c/--create. The queue is automatically made
3105 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
3106 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
3107 currently active queue in the repository. Then the queue will only be
3111 created and switching will fail.
3108 created and switching will fail.
3112
3109
3113 To delete an existing queue, use --delete. You cannot delete the currently
3110 To delete an existing queue, use --delete. You cannot delete the currently
3114 active queue.
3111 active queue.
3115
3112
3116 Returns 0 on success.
3113 Returns 0 on success.
3117 '''
3114 '''
3118 q = repo.mq
3115 q = repo.mq
3119 _defaultqueue = 'patches'
3116 _defaultqueue = 'patches'
3120 _allqueues = 'patches.queues'
3117 _allqueues = 'patches.queues'
3121 _activequeue = 'patches.queue'
3118 _activequeue = 'patches.queue'
3122
3119
3123 def _getcurrent():
3120 def _getcurrent():
3124 cur = os.path.basename(q.path)
3121 cur = os.path.basename(q.path)
3125 if cur.startswith('patches-'):
3122 if cur.startswith('patches-'):
3126 cur = cur[8:]
3123 cur = cur[8:]
3127 return cur
3124 return cur
3128
3125
3129 def _noqueues():
3126 def _noqueues():
3130 try:
3127 try:
3131 fh = repo.opener(_allqueues, 'r')
3128 fh = repo.opener(_allqueues, 'r')
3132 fh.close()
3129 fh.close()
3133 except IOError:
3130 except IOError:
3134 return True
3131 return True
3135
3132
3136 return False
3133 return False
3137
3134
3138 def _getqueues():
3135 def _getqueues():
3139 current = _getcurrent()
3136 current = _getcurrent()
3140
3137
3141 try:
3138 try:
3142 fh = repo.opener(_allqueues, 'r')
3139 fh = repo.opener(_allqueues, 'r')
3143 queues = [queue.strip() for queue in fh if queue.strip()]
3140 queues = [queue.strip() for queue in fh if queue.strip()]
3144 fh.close()
3141 fh.close()
3145 if current not in queues:
3142 if current not in queues:
3146 queues.append(current)
3143 queues.append(current)
3147 except IOError:
3144 except IOError:
3148 queues = [_defaultqueue]
3145 queues = [_defaultqueue]
3149
3146
3150 return sorted(queues)
3147 return sorted(queues)
3151
3148
3152 def _setactive(name):
3149 def _setactive(name):
3153 if q.applied:
3150 if q.applied:
3154 raise util.Abort(_('new queue created, but cannot make active '
3151 raise util.Abort(_('new queue created, but cannot make active '
3155 'as patches are applied'))
3152 'as patches are applied'))
3156 _setactivenocheck(name)
3153 _setactivenocheck(name)
3157
3154
3158 def _setactivenocheck(name):
3155 def _setactivenocheck(name):
3159 fh = repo.opener(_activequeue, 'w')
3156 fh = repo.opener(_activequeue, 'w')
3160 if name != 'patches':
3157 if name != 'patches':
3161 fh.write(name)
3158 fh.write(name)
3162 fh.close()
3159 fh.close()
3163
3160
3164 def _addqueue(name):
3161 def _addqueue(name):
3165 fh = repo.opener(_allqueues, 'a')
3162 fh = repo.opener(_allqueues, 'a')
3166 fh.write('%s\n' % (name,))
3163 fh.write('%s\n' % (name,))
3167 fh.close()
3164 fh.close()
3168
3165
3169 def _queuedir(name):
3166 def _queuedir(name):
3170 if name == 'patches':
3167 if name == 'patches':
3171 return repo.join('patches')
3168 return repo.join('patches')
3172 else:
3169 else:
3173 return repo.join('patches-' + name)
3170 return repo.join('patches-' + name)
3174
3171
3175 def _validname(name):
3172 def _validname(name):
3176 for n in name:
3173 for n in name:
3177 if n in ':\\/.':
3174 if n in ':\\/.':
3178 return False
3175 return False
3179 return True
3176 return True
3180
3177
3181 def _delete(name):
3178 def _delete(name):
3182 if name not in existing:
3179 if name not in existing:
3183 raise util.Abort(_('cannot delete queue that does not exist'))
3180 raise util.Abort(_('cannot delete queue that does not exist'))
3184
3181
3185 current = _getcurrent()
3182 current = _getcurrent()
3186
3183
3187 if name == current:
3184 if name == current:
3188 raise util.Abort(_('cannot delete currently active queue'))
3185 raise util.Abort(_('cannot delete currently active queue'))
3189
3186
3190 fh = repo.opener('patches.queues.new', 'w')
3187 fh = repo.opener('patches.queues.new', 'w')
3191 for queue in existing:
3188 for queue in existing:
3192 if queue == name:
3189 if queue == name:
3193 continue
3190 continue
3194 fh.write('%s\n' % (queue,))
3191 fh.write('%s\n' % (queue,))
3195 fh.close()
3192 fh.close()
3196 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3193 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3197
3194
3198 if not name or opts.get('list') or opts.get('active'):
3195 if not name or opts.get('list') or opts.get('active'):
3199 current = _getcurrent()
3196 current = _getcurrent()
3200 if opts.get('active'):
3197 if opts.get('active'):
3201 ui.write('%s\n' % (current,))
3198 ui.write('%s\n' % (current,))
3202 return
3199 return
3203 for queue in _getqueues():
3200 for queue in _getqueues():
3204 ui.write('%s' % (queue,))
3201 ui.write('%s' % (queue,))
3205 if queue == current and not ui.quiet:
3202 if queue == current and not ui.quiet:
3206 ui.write(_(' (active)\n'))
3203 ui.write(_(' (active)\n'))
3207 else:
3204 else:
3208 ui.write('\n')
3205 ui.write('\n')
3209 return
3206 return
3210
3207
3211 if not _validname(name):
3208 if not _validname(name):
3212 raise util.Abort(
3209 raise util.Abort(
3213 _('invalid queue name, may not contain the characters ":\\/."'))
3210 _('invalid queue name, may not contain the characters ":\\/."'))
3214
3211
3215 existing = _getqueues()
3212 existing = _getqueues()
3216
3213
3217 if opts.get('create'):
3214 if opts.get('create'):
3218 if name in existing:
3215 if name in existing:
3219 raise util.Abort(_('queue "%s" already exists') % name)
3216 raise util.Abort(_('queue "%s" already exists') % name)
3220 if _noqueues():
3217 if _noqueues():
3221 _addqueue(_defaultqueue)
3218 _addqueue(_defaultqueue)
3222 _addqueue(name)
3219 _addqueue(name)
3223 _setactive(name)
3220 _setactive(name)
3224 elif opts.get('rename'):
3221 elif opts.get('rename'):
3225 current = _getcurrent()
3222 current = _getcurrent()
3226 if name == current:
3223 if name == current:
3227 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
3224 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
3228 if name in existing:
3225 if name in existing:
3229 raise util.Abort(_('queue "%s" already exists') % name)
3226 raise util.Abort(_('queue "%s" already exists') % name)
3230
3227
3231 olddir = _queuedir(current)
3228 olddir = _queuedir(current)
3232 newdir = _queuedir(name)
3229 newdir = _queuedir(name)
3233
3230
3234 if os.path.exists(newdir):
3231 if os.path.exists(newdir):
3235 raise util.Abort(_('non-queue directory "%s" already exists') %
3232 raise util.Abort(_('non-queue directory "%s" already exists') %
3236 newdir)
3233 newdir)
3237
3234
3238 fh = repo.opener('patches.queues.new', 'w')
3235 fh = repo.opener('patches.queues.new', 'w')
3239 for queue in existing:
3236 for queue in existing:
3240 if queue == current:
3237 if queue == current:
3241 fh.write('%s\n' % (name,))
3238 fh.write('%s\n' % (name,))
3242 if os.path.exists(olddir):
3239 if os.path.exists(olddir):
3243 util.rename(olddir, newdir)
3240 util.rename(olddir, newdir)
3244 else:
3241 else:
3245 fh.write('%s\n' % (queue,))
3242 fh.write('%s\n' % (queue,))
3246 fh.close()
3243 fh.close()
3247 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3244 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3248 _setactivenocheck(name)
3245 _setactivenocheck(name)
3249 elif opts.get('delete'):
3246 elif opts.get('delete'):
3250 _delete(name)
3247 _delete(name)
3251 elif opts.get('purge'):
3248 elif opts.get('purge'):
3252 if name in existing:
3249 if name in existing:
3253 _delete(name)
3250 _delete(name)
3254 qdir = _queuedir(name)
3251 qdir = _queuedir(name)
3255 if os.path.exists(qdir):
3252 if os.path.exists(qdir):
3256 shutil.rmtree(qdir)
3253 shutil.rmtree(qdir)
3257 else:
3254 else:
3258 if name not in existing:
3255 if name not in existing:
3259 raise util.Abort(_('use --create to create a new queue'))
3256 raise util.Abort(_('use --create to create a new queue'))
3260 _setactive(name)
3257 _setactive(name)
3261
3258
3262 def mqphasedefaults(repo, roots):
3259 def mqphasedefaults(repo, roots):
3263 """callback used to set mq changeset as secret when no phase data exists"""
3260 """callback used to set mq changeset as secret when no phase data exists"""
3264 if repo.mq.applied:
3261 if repo.mq.applied:
3265 if repo.ui.configbool('mq', 'secret', False):
3262 if repo.ui.configbool('mq', 'secret', False):
3266 mqphase = phases.secret
3263 mqphase = phases.secret
3267 else:
3264 else:
3268 mqphase = phases.draft
3265 mqphase = phases.draft
3269 qbase = repo[repo.mq.applied[0].node]
3266 qbase = repo[repo.mq.applied[0].node]
3270 roots[mqphase].add(qbase.node())
3267 roots[mqphase].add(qbase.node())
3271 return roots
3268 return roots
3272
3269
3273 def reposetup(ui, repo):
3270 def reposetup(ui, repo):
3274 class mqrepo(repo.__class__):
3271 class mqrepo(repo.__class__):
3275 @localrepo.unfilteredpropertycache
3272 @localrepo.unfilteredpropertycache
3276 def mq(self):
3273 def mq(self):
3277 return queue(self.ui, self.baseui, self.path)
3274 return queue(self.ui, self.baseui, self.path)
3278
3275
3279 def invalidateall(self):
3276 def invalidateall(self):
3280 super(mqrepo, self).invalidateall()
3277 super(mqrepo, self).invalidateall()
3281 if localrepo.hasunfilteredcache(self, 'mq'):
3278 if localrepo.hasunfilteredcache(self, 'mq'):
3282 # recreate mq in case queue path was changed
3279 # recreate mq in case queue path was changed
3283 delattr(self.unfiltered(), 'mq')
3280 delattr(self.unfiltered(), 'mq')
3284
3281
3285 def abortifwdirpatched(self, errmsg, force=False):
3282 def abortifwdirpatched(self, errmsg, force=False):
3286 if self.mq.applied and self.mq.checkapplied and not force:
3283 if self.mq.applied and self.mq.checkapplied and not force:
3287 parents = self.dirstate.parents()
3284 parents = self.dirstate.parents()
3288 patches = [s.node for s in self.mq.applied]
3285 patches = [s.node for s in self.mq.applied]
3289 if parents[0] in patches or parents[1] in patches:
3286 if parents[0] in patches or parents[1] in patches:
3290 raise util.Abort(errmsg)
3287 raise util.Abort(errmsg)
3291
3288
3292 def commit(self, text="", user=None, date=None, match=None,
3289 def commit(self, text="", user=None, date=None, match=None,
3293 force=False, editor=False, extra={}):
3290 force=False, editor=False, extra={}):
3294 self.abortifwdirpatched(
3291 self.abortifwdirpatched(
3295 _('cannot commit over an applied mq patch'),
3292 _('cannot commit over an applied mq patch'),
3296 force)
3293 force)
3297
3294
3298 return super(mqrepo, self).commit(text, user, date, match, force,
3295 return super(mqrepo, self).commit(text, user, date, match, force,
3299 editor, extra)
3296 editor, extra)
3300
3297
3301 def checkpush(self, pushop):
3298 def checkpush(self, pushop):
3302 if self.mq.applied and self.mq.checkapplied and not pushop.force:
3299 if self.mq.applied and self.mq.checkapplied and not pushop.force:
3303 outapplied = [e.node for e in self.mq.applied]
3300 outapplied = [e.node for e in self.mq.applied]
3304 if pushop.revs:
3301 if pushop.revs:
3305 # Assume applied patches have no non-patch descendants and
3302 # Assume applied patches have no non-patch descendants and
3306 # are not on remote already. Filtering any changeset not
3303 # are not on remote already. Filtering any changeset not
3307 # pushed.
3304 # pushed.
3308 heads = set(pushop.revs)
3305 heads = set(pushop.revs)
3309 for node in reversed(outapplied):
3306 for node in reversed(outapplied):
3310 if node in heads:
3307 if node in heads:
3311 break
3308 break
3312 else:
3309 else:
3313 outapplied.pop()
3310 outapplied.pop()
3314 # looking for pushed and shared changeset
3311 # looking for pushed and shared changeset
3315 for node in outapplied:
3312 for node in outapplied:
3316 if self[node].phase() < phases.secret:
3313 if self[node].phase() < phases.secret:
3317 raise util.Abort(_('source has mq patches applied'))
3314 raise util.Abort(_('source has mq patches applied'))
3318 # no non-secret patches pushed
3315 # no non-secret patches pushed
3319 super(mqrepo, self).checkpush(pushop)
3316 super(mqrepo, self).checkpush(pushop)
3320
3317
3321 def _findtags(self):
3318 def _findtags(self):
3322 '''augment tags from base class with patch tags'''
3319 '''augment tags from base class with patch tags'''
3323 result = super(mqrepo, self)._findtags()
3320 result = super(mqrepo, self)._findtags()
3324
3321
3325 q = self.mq
3322 q = self.mq
3326 if not q.applied:
3323 if not q.applied:
3327 return result
3324 return result
3328
3325
3329 mqtags = [(patch.node, patch.name) for patch in q.applied]
3326 mqtags = [(patch.node, patch.name) for patch in q.applied]
3330
3327
3331 try:
3328 try:
3332 # for now ignore filtering business
3329 # for now ignore filtering business
3333 self.unfiltered().changelog.rev(mqtags[-1][0])
3330 self.unfiltered().changelog.rev(mqtags[-1][0])
3334 except error.LookupError:
3331 except error.LookupError:
3335 self.ui.warn(_('mq status file refers to unknown node %s\n')
3332 self.ui.warn(_('mq status file refers to unknown node %s\n')
3336 % short(mqtags[-1][0]))
3333 % short(mqtags[-1][0]))
3337 return result
3334 return result
3338
3335
3339 # do not add fake tags for filtered revisions
3336 # do not add fake tags for filtered revisions
3340 included = self.changelog.hasnode
3337 included = self.changelog.hasnode
3341 mqtags = [mqt for mqt in mqtags if included(mqt[0])]
3338 mqtags = [mqt for mqt in mqtags if included(mqt[0])]
3342 if not mqtags:
3339 if not mqtags:
3343 return result
3340 return result
3344
3341
3345 mqtags.append((mqtags[-1][0], 'qtip'))
3342 mqtags.append((mqtags[-1][0], 'qtip'))
3346 mqtags.append((mqtags[0][0], 'qbase'))
3343 mqtags.append((mqtags[0][0], 'qbase'))
3347 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3344 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3348 tags = result[0]
3345 tags = result[0]
3349 for patch in mqtags:
3346 for patch in mqtags:
3350 if patch[1] in tags:
3347 if patch[1] in tags:
3351 self.ui.warn(_('tag %s overrides mq patch of the same '
3348 self.ui.warn(_('tag %s overrides mq patch of the same '
3352 'name\n') % patch[1])
3349 'name\n') % patch[1])
3353 else:
3350 else:
3354 tags[patch[1]] = patch[0]
3351 tags[patch[1]] = patch[0]
3355
3352
3356 return result
3353 return result
3357
3354
3358 if repo.local():
3355 if repo.local():
3359 repo.__class__ = mqrepo
3356 repo.__class__ = mqrepo
3360
3357
3361 repo._phasedefaults.append(mqphasedefaults)
3358 repo._phasedefaults.append(mqphasedefaults)
3362
3359
3363 def mqimport(orig, ui, repo, *args, **kwargs):
3360 def mqimport(orig, ui, repo, *args, **kwargs):
3364 if (util.safehasattr(repo, 'abortifwdirpatched')
3361 if (util.safehasattr(repo, 'abortifwdirpatched')
3365 and not kwargs.get('no_commit', False)):
3362 and not kwargs.get('no_commit', False)):
3366 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3363 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3367 kwargs.get('force'))
3364 kwargs.get('force'))
3368 return orig(ui, repo, *args, **kwargs)
3365 return orig(ui, repo, *args, **kwargs)
3369
3366
3370 def mqinit(orig, ui, *args, **kwargs):
3367 def mqinit(orig, ui, *args, **kwargs):
3371 mq = kwargs.pop('mq', None)
3368 mq = kwargs.pop('mq', None)
3372
3369
3373 if not mq:
3370 if not mq:
3374 return orig(ui, *args, **kwargs)
3371 return orig(ui, *args, **kwargs)
3375
3372
3376 if args:
3373 if args:
3377 repopath = args[0]
3374 repopath = args[0]
3378 if not hg.islocal(repopath):
3375 if not hg.islocal(repopath):
3379 raise util.Abort(_('only a local queue repository '
3376 raise util.Abort(_('only a local queue repository '
3380 'may be initialized'))
3377 'may be initialized'))
3381 else:
3378 else:
3382 repopath = cmdutil.findrepo(os.getcwd())
3379 repopath = cmdutil.findrepo(os.getcwd())
3383 if not repopath:
3380 if not repopath:
3384 raise util.Abort(_('there is no Mercurial repository here '
3381 raise util.Abort(_('there is no Mercurial repository here '
3385 '(.hg not found)'))
3382 '(.hg not found)'))
3386 repo = hg.repository(ui, repopath)
3383 repo = hg.repository(ui, repopath)
3387 return qinit(ui, repo, True)
3384 return qinit(ui, repo, True)
3388
3385
3389 def mqcommand(orig, ui, repo, *args, **kwargs):
3386 def mqcommand(orig, ui, repo, *args, **kwargs):
3390 """Add --mq option to operate on patch repository instead of main"""
3387 """Add --mq option to operate on patch repository instead of main"""
3391
3388
3392 # some commands do not like getting unknown options
3389 # some commands do not like getting unknown options
3393 mq = kwargs.pop('mq', None)
3390 mq = kwargs.pop('mq', None)
3394
3391
3395 if not mq:
3392 if not mq:
3396 return orig(ui, repo, *args, **kwargs)
3393 return orig(ui, repo, *args, **kwargs)
3397
3394
3398 q = repo.mq
3395 q = repo.mq
3399 r = q.qrepo()
3396 r = q.qrepo()
3400 if not r:
3397 if not r:
3401 raise util.Abort(_('no queue repository'))
3398 raise util.Abort(_('no queue repository'))
3402 return orig(r.ui, r, *args, **kwargs)
3399 return orig(r.ui, r, *args, **kwargs)
3403
3400
3404 def summaryhook(ui, repo):
3401 def summaryhook(ui, repo):
3405 q = repo.mq
3402 q = repo.mq
3406 m = []
3403 m = []
3407 a, u = len(q.applied), len(q.unapplied(repo))
3404 a, u = len(q.applied), len(q.unapplied(repo))
3408 if a:
3405 if a:
3409 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3406 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3410 if u:
3407 if u:
3411 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3408 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3412 if m:
3409 if m:
3413 # i18n: column positioning for "hg summary"
3410 # i18n: column positioning for "hg summary"
3414 ui.write(_("mq: %s\n") % ', '.join(m))
3411 ui.write(_("mq: %s\n") % ', '.join(m))
3415 else:
3412 else:
3416 # i18n: column positioning for "hg summary"
3413 # i18n: column positioning for "hg summary"
3417 ui.note(_("mq: (empty queue)\n"))
3414 ui.note(_("mq: (empty queue)\n"))
3418
3415
3419 def revsetmq(repo, subset, x):
3416 def revsetmq(repo, subset, x):
3420 """``mq()``
3417 """``mq()``
3421 Changesets managed by MQ.
3418 Changesets managed by MQ.
3422 """
3419 """
3423 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3420 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3424 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3421 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])
3422 return revset.baseset([r for r in subset if r in applied])
3426
3423
3427 # tell hggettext to extract docstrings from these functions:
3424 # tell hggettext to extract docstrings from these functions:
3428 i18nfunctions = [revsetmq]
3425 i18nfunctions = [revsetmq]
3429
3426
3430 def extsetup(ui):
3427 def extsetup(ui):
3431 # Ensure mq wrappers are called first, regardless of extension load order by
3428 # 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.
3429 # NOT wrapping in uisetup() and instead deferring to init stage two here.
3433 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3430 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3434
3431
3435 extensions.wrapcommand(commands.table, 'import', mqimport)
3432 extensions.wrapcommand(commands.table, 'import', mqimport)
3436 cmdutil.summaryhooks.add('mq', summaryhook)
3433 cmdutil.summaryhooks.add('mq', summaryhook)
3437
3434
3438 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3435 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3439 entry[1].extend(mqopt)
3436 entry[1].extend(mqopt)
3440
3437
3441 nowrap = set(commands.norepo.split(" "))
3438 nowrap = set(commands.norepo.split(" "))
3442
3439
3443 def dotable(cmdtable):
3440 def dotable(cmdtable):
3444 for cmd in cmdtable.keys():
3441 for cmd in cmdtable.keys():
3445 cmd = cmdutil.parsealiases(cmd)[0]
3442 cmd = cmdutil.parsealiases(cmd)[0]
3446 if cmd in nowrap:
3443 if cmd in nowrap:
3447 continue
3444 continue
3448 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3445 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3449 entry[1].extend(mqopt)
3446 entry[1].extend(mqopt)
3450
3447
3451 dotable(commands.table)
3448 dotable(commands.table)
3452
3449
3453 for extname, extmodule in extensions.extensions():
3450 for extname, extmodule in extensions.extensions():
3454 if extmodule.__file__ != __file__:
3451 if extmodule.__file__ != __file__:
3455 dotable(getattr(extmodule, 'cmdtable', {}))
3452 dotable(getattr(extmodule, 'cmdtable', {}))
3456
3453
3457 revset.symbols['mq'] = revsetmq
3454 revset.symbols['mq'] = revsetmq
3458
3455
3459 colortable = {'qguard.negative': 'red',
3456 colortable = {'qguard.negative': 'red',
3460 'qguard.positive': 'yellow',
3457 'qguard.positive': 'yellow',
3461 'qguard.unguarded': 'green',
3458 'qguard.unguarded': 'green',
3462 'qseries.applied': 'blue bold underline',
3459 'qseries.applied': 'blue bold underline',
3463 'qseries.guarded': 'black bold',
3460 'qseries.guarded': 'black bold',
3464 'qseries.missing': 'red bold',
3461 'qseries.missing': 'red bold',
3465 'qseries.unapplied': 'black bold'}
3462 'qseries.unapplied': 'black bold'}
3466
3463
3467 commands.inferrepo += " qnew qrefresh qdiff qcommit"
3464 commands.inferrepo += " qnew qrefresh qdiff qcommit"
@@ -1,158 +1,168 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 (this tests also that editor is not invoked if '--edit' is not
9 (this tests also that editor is not invoked if '--edit' is not
10 specified)
10 specified)
11
11
12 $ hg qrefresh
12 $ hg qrefresh
13 no patches applied
13 no patches applied
14 [1]
14 [1]
15 $ hg qrefresh -e
15 $ hg qrefresh -e
16 no patches applied
16 no patches applied
17 [1]
17 [1]
18 $ hg qnew -m "First commit message" first-patch
18 $ hg qnew -m "First commit message" first-patch
19 $ echo aaaa > file
19 $ echo aaaa > file
20 $ hg add file
20 $ hg add file
21 $ HGEDITOR=cat hg qrefresh
21 $ HGEDITOR=cat hg qrefresh
22
22
23 Should display 'First commit message'
23 Should display 'First commit message'
24
24
25 $ hg log -l1 --template "{desc}\n"
25 $ hg log -l1 --template "{desc}\n"
26 First commit message
26 First commit message
27
27
28 Testing changing message with -m
28 Testing changing message with -m
29 (this tests also that '--edit' can be used with '--message')
29
30
30 $ echo bbbb > file
31 $ echo bbbb > file
31 $ hg qrefresh -m "Second commit message"
32 $ HGEDITOR=cat hg qrefresh -m "Second commit message" -e
33 Second commit message
34
35
36 HG: Enter commit message. Lines beginning with 'HG:' are removed.
37 HG: Leave message empty to use default message.
38 HG: --
39 HG: user: test
40 HG: branch 'default'
41 HG: added file
32
42
33 Should display 'Second commit message'
43 Should display 'Second commit message'
34
44
35 $ hg log -l1 --template "{desc}\n"
45 $ hg log -l1 --template "{desc}\n"
36 Second commit message
46 Second commit message
37
47
38 Testing changing message with -l
48 Testing changing message with -l
39
49
40 $ echo "Third commit message" > logfile
50 $ echo "Third commit message" > logfile
41 $ echo " This is the 3rd log message" >> logfile
51 $ echo " This is the 3rd log message" >> logfile
42 $ echo bbbb > file
52 $ echo bbbb > file
43 $ hg qrefresh -l logfile
53 $ hg qrefresh -l logfile
44
54
45 Should display 'Third commit message\\\n This is the 3rd log message'
55 Should display 'Third commit message\\\n This is the 3rd log message'
46
56
47 $ hg log -l1 --template "{desc}\n"
57 $ hg log -l1 --template "{desc}\n"
48 Third commit message
58 Third commit message
49 This is the 3rd log message
59 This is the 3rd log message
50
60
51 Testing changing message with -l-
61 Testing changing message with -l-
52
62
53 $ hg qnew -m "First commit message" second-patch
63 $ hg qnew -m "First commit message" second-patch
54 $ echo aaaa > file2
64 $ echo aaaa > file2
55 $ hg add file2
65 $ hg add file2
56 $ echo bbbb > file2
66 $ echo bbbb > file2
57 $ (echo "Fifth commit message"; echo " This is the 5th log message") | hg qrefresh -l-
67 $ (echo "Fifth commit message"; echo " This is the 5th log message") | hg qrefresh -l-
58
68
59 Should display 'Fifth commit message\\\n This is the 5th log message'
69 Should display 'Fifth commit message\\\n This is the 5th log message'
60
70
61 $ hg log -l1 --template "{desc}\n"
71 $ hg log -l1 --template "{desc}\n"
62 Fifth commit message
72 Fifth commit message
63 This is the 5th log message
73 This is the 5th log message
64
74
65 Test saving last-message.txt:
75 Test saving last-message.txt:
66
76
67 $ cat > $TESTTMP/editor.sh << EOF
77 $ cat > $TESTTMP/editor.sh << EOF
68 > echo "==== before editing"
78 > echo "==== before editing"
69 > cat \$1
79 > cat \$1
70 > echo "===="
80 > echo "===="
71 > (echo; echo "test saving last-message.txt") >> \$1
81 > (echo; echo "test saving last-message.txt") >> \$1
72 > EOF
82 > EOF
73
83
74 $ cat > $TESTTMP/commitfailure.py <<EOF
84 $ cat > $TESTTMP/commitfailure.py <<EOF
75 > from mercurial import util
85 > from mercurial import util
76 > def reposetup(ui, repo):
86 > def reposetup(ui, repo):
77 > class commitfailure(repo.__class__):
87 > class commitfailure(repo.__class__):
78 > def commit(self, *args, **kwargs):
88 > def commit(self, *args, **kwargs):
79 > raise util.Abort('emulating unexpected abort')
89 > raise util.Abort('emulating unexpected abort')
80 > repo.__class__ = commitfailure
90 > repo.__class__ = commitfailure
81 > EOF
91 > EOF
82
92
83 $ cat >> .hg/hgrc <<EOF
93 $ cat >> .hg/hgrc <<EOF
84 > [extensions]
94 > [extensions]
85 > # this failure occurs before editor invocation
95 > # this failure occurs before editor invocation
86 > commitfailure = $TESTTMP/commitfailure.py
96 > commitfailure = $TESTTMP/commitfailure.py
87 > EOF
97 > EOF
88
98
89 $ hg qapplied
99 $ hg qapplied
90 first-patch
100 first-patch
91 second-patch
101 second-patch
92 $ hg tip --template "{files}\n"
102 $ hg tip --template "{files}\n"
93 file2
103 file2
94
104
95 (test that editor is not invoked before transaction starting)
105 (test that editor is not invoked before transaction starting)
96
106
97 $ rm -f .hg/last-message.txt
107 $ rm -f .hg/last-message.txt
98 $ HGEDITOR="sh $TESTTMP/editor.sh" hg qrefresh -e
108 $ HGEDITOR="sh $TESTTMP/editor.sh" hg qrefresh -e
99 refresh interrupted while patch was popped! (revert --all, qpush to recover)
109 refresh interrupted while patch was popped! (revert --all, qpush to recover)
100 abort: emulating unexpected abort
110 abort: emulating unexpected abort
101 [255]
111 [255]
102 $ cat .hg/last-message.txt
112 $ cat .hg/last-message.txt
103 cat: .hg/last-message.txt: No such file or directory
113 cat: .hg/last-message.txt: No such file or directory
104 [1]
114 [1]
105
115
106 (reset applied patches and directory status)
116 (reset applied patches and directory status)
107
117
108 $ cat >> .hg/hgrc <<EOF
118 $ cat >> .hg/hgrc <<EOF
109 > [extensions]
119 > [extensions]
110 > commitfailure = !
120 > commitfailure = !
111 > EOF
121 > EOF
112
122
113 $ hg qapplied
123 $ hg qapplied
114 first-patch
124 first-patch
115 $ hg status -A file2
125 $ hg status -A file2
116 ? file2
126 ? file2
117 $ rm file2
127 $ rm file2
118 $ hg qpush -q second-patch
128 $ hg qpush -q second-patch
119 now at: second-patch
129 now at: second-patch
120
130
121 (test that editor is invoked and commit message is saved into
131 (test that editor is invoked and commit message is saved into
122 "last-message.txt")
132 "last-message.txt")
123
133
124 $ cat >> .hg/hgrc <<EOF
134 $ cat >> .hg/hgrc <<EOF
125 > [hooks]
135 > [hooks]
126 > # this failure occurs after editor invocation
136 > # this failure occurs after editor invocation
127 > pretxncommit.unexpectedabort = false
137 > pretxncommit.unexpectedabort = false
128 > EOF
138 > EOF
129
139
130 $ rm -f .hg/last-message.txt
140 $ rm -f .hg/last-message.txt
131 $ hg status --rev "second-patch^1" -arm
141 $ hg status --rev "second-patch^1" -arm
132 A file2
142 A file2
133 $ HGEDITOR="sh $TESTTMP/editor.sh" hg qrefresh -e
143 $ HGEDITOR="sh $TESTTMP/editor.sh" hg qrefresh -e
134 ==== before editing
144 ==== before editing
135 Fifth commit message
145 Fifth commit message
136 This is the 5th log message
146 This is the 5th log message
137
147
138
148
139 HG: Enter commit message. Lines beginning with 'HG:' are removed.
149 HG: Enter commit message. Lines beginning with 'HG:' are removed.
140 HG: Leave message empty to use default message.
150 HG: Leave message empty to use default message.
141 HG: --
151 HG: --
142 HG: user: test
152 HG: user: test
143 HG: branch 'default'
153 HG: branch 'default'
144 HG: added file2
154 HG: added file2
145 ====
155 ====
146 transaction abort!
156 transaction abort!
147 rollback completed
157 rollback completed
148 note: commit message saved in .hg/last-message.txt
158 note: commit message saved in .hg/last-message.txt
149 refresh interrupted while patch was popped! (revert --all, qpush to recover)
159 refresh interrupted while patch was popped! (revert --all, qpush to recover)
150 abort: pretxncommit.unexpectedabort hook exited with status 1
160 abort: pretxncommit.unexpectedabort hook exited with status 1
151 [255]
161 [255]
152 $ cat .hg/last-message.txt
162 $ cat .hg/last-message.txt
153 Fifth commit message
163 Fifth commit message
154 This is the 5th log message
164 This is the 5th log message
155
165
156
166
157
167
158 test saving last-message.txt
168 test saving last-message.txt
General Comments 0
You need to be logged in to leave comments. Login now