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