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