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