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