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