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