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