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