##// END OF EJS Templates
merge with stable
Matt Mackall -
r16279:531e69ff merge default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

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