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