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