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