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