##// END OF EJS Templates
cmdserver: recreate mq object on runcommand in case queue path was changed...
Yuya Nishihara -
r20629:277dc5f2 default
parent child Browse files
Show More
@@ -1,3459 +1,3460 b''
1 # mq.py - patch queues for mercurial
1 # mq.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''manage a stack of patches
8 '''manage a stack of patches
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use :hg:`help command` for more details)::
17 Common tasks (use :hg:`help command` for more details)::
18
18
19 create new patch qnew
19 create new patch qnew
20 import existing patch qimport
20 import existing patch qimport
21
21
22 print patch series qseries
22 print patch series qseries
23 print applied patches qapplied
23 print applied patches qapplied
24
24
25 add known patch to applied stack qpush
25 add known patch to applied stack qpush
26 remove patch from applied stack qpop
26 remove patch from applied stack qpop
27 refresh contents of top applied patch qrefresh
27 refresh contents of top applied patch qrefresh
28
28
29 By default, mq will automatically use git patches when required to
29 By default, mq will automatically use git patches when required to
30 avoid losing file mode changes, copy records, binary files or empty
30 avoid losing file mode changes, copy records, binary files or empty
31 files creations or deletions. This behaviour can be configured with::
31 files creations or deletions. This behaviour can be configured with::
32
32
33 [mq]
33 [mq]
34 git = auto/keep/yes/no
34 git = auto/keep/yes/no
35
35
36 If set to 'keep', mq will obey the [diff] section configuration while
36 If set to 'keep', mq will obey the [diff] section configuration while
37 preserving existing git patches upon qrefresh. If set to 'yes' or
37 preserving existing git patches upon qrefresh. If set to 'yes' or
38 'no', mq will override the [diff] section and always generate git or
38 'no', mq will override the [diff] section and always generate git or
39 regular patches, possibly losing data in the second case.
39 regular patches, possibly losing data in the second case.
40
40
41 It may be desirable for mq changesets to be kept in the secret phase (see
41 It may be desirable for mq changesets to be kept in the secret phase (see
42 :hg:`help phases`), which can be enabled with the following setting::
42 :hg:`help phases`), which can be enabled with the following setting::
43
43
44 [mq]
44 [mq]
45 secret = True
45 secret = True
46
46
47 You will by default be managing a patch queue named "patches". You can
47 You will by default be managing a patch queue named "patches". You can
48 create other, independent patch queues with the :hg:`qqueue` command.
48 create other, independent patch queues with the :hg:`qqueue` command.
49
49
50 If the working directory contains uncommitted files, qpush, qpop and
50 If the working directory contains uncommitted files, qpush, qpop and
51 qgoto abort immediately. If -f/--force is used, the changes are
51 qgoto abort immediately. If -f/--force is used, the changes are
52 discarded. Setting::
52 discarded. Setting::
53
53
54 [mq]
54 [mq]
55 keepchanges = True
55 keepchanges = True
56
56
57 make them behave as if --keep-changes were passed, and non-conflicting
57 make them behave as if --keep-changes were passed, and non-conflicting
58 local changes will be tolerated and preserved. If incompatible options
58 local changes will be tolerated and preserved. If incompatible options
59 such as -f/--force or --exact are passed, this setting is ignored.
59 such as -f/--force or --exact are passed, this setting is ignored.
60
60
61 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')
1038 inclsubs.append('.hgsubstate')
1039 substatestate = repo.dirstate['.hgsubstate']
1039 substatestate = repo.dirstate['.hgsubstate']
1040 if opts.get('include') or opts.get('exclude') or pats:
1040 if opts.get('include') or opts.get('exclude') or pats:
1041 if inclsubs:
1041 if inclsubs:
1042 pats = list(pats or []) + inclsubs
1042 pats = list(pats or []) + inclsubs
1043 match = scmutil.match(repo[None], pats, opts)
1043 match = scmutil.match(repo[None], pats, opts)
1044 # detect missing files in pats
1044 # detect missing files in pats
1045 def badfn(f, msg):
1045 def badfn(f, msg):
1046 if f != '.hgsubstate': # .hgsubstate is auto-created
1046 if f != '.hgsubstate': # .hgsubstate is auto-created
1047 raise util.Abort('%s: %s' % (f, msg))
1047 raise util.Abort('%s: %s' % (f, msg))
1048 match.bad = badfn
1048 match.bad = badfn
1049 changes = repo.status(match=match)
1049 changes = repo.status(match=match)
1050 m, a, r, d = changes[:4]
1050 m, a, r, d = changes[:4]
1051 else:
1051 else:
1052 changes = self.checklocalchanges(repo, force=True)
1052 changes = self.checklocalchanges(repo, force=True)
1053 m, a, r, d = changes
1053 m, a, r, d = changes
1054 match = scmutil.matchfiles(repo, m + a + r + inclsubs)
1054 match = scmutil.matchfiles(repo, m + a + r + inclsubs)
1055 if len(repo[None].parents()) > 1:
1055 if len(repo[None].parents()) > 1:
1056 raise util.Abort(_('cannot manage merge changesets'))
1056 raise util.Abort(_('cannot manage merge changesets'))
1057 commitfiles = m + a + r
1057 commitfiles = m + a + r
1058 self.checktoppatch(repo)
1058 self.checktoppatch(repo)
1059 insert = self.fullseriesend()
1059 insert = self.fullseriesend()
1060 wlock = repo.wlock()
1060 wlock = repo.wlock()
1061 try:
1061 try:
1062 try:
1062 try:
1063 # if patch file write fails, abort early
1063 # if patch file write fails, abort early
1064 p = self.opener(patchfn, "w")
1064 p = self.opener(patchfn, "w")
1065 except IOError, e:
1065 except IOError, e:
1066 raise util.Abort(_('cannot write patch "%s": %s')
1066 raise util.Abort(_('cannot write patch "%s": %s')
1067 % (patchfn, e.strerror))
1067 % (patchfn, e.strerror))
1068 try:
1068 try:
1069 if self.plainmode:
1069 if self.plainmode:
1070 if user:
1070 if user:
1071 p.write("From: " + user + "\n")
1071 p.write("From: " + user + "\n")
1072 if not date:
1072 if not date:
1073 p.write("\n")
1073 p.write("\n")
1074 if date:
1074 if date:
1075 p.write("Date: %d %d\n\n" % date)
1075 p.write("Date: %d %d\n\n" % date)
1076 else:
1076 else:
1077 p.write("# HG changeset patch\n")
1077 p.write("# HG changeset patch\n")
1078 p.write("# Parent "
1078 p.write("# Parent "
1079 + hex(repo[None].p1().node()) + "\n")
1079 + hex(repo[None].p1().node()) + "\n")
1080 if user:
1080 if user:
1081 p.write("# User " + user + "\n")
1081 p.write("# User " + user + "\n")
1082 if date:
1082 if date:
1083 p.write("# Date %s %s\n\n" % date)
1083 p.write("# Date %s %s\n\n" % date)
1084 if util.safehasattr(msg, '__call__'):
1084 if util.safehasattr(msg, '__call__'):
1085 msg = msg()
1085 msg = msg()
1086 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
1086 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
1087 n = newcommit(repo, None, commitmsg, user, date, match=match,
1087 n = newcommit(repo, None, commitmsg, user, date, match=match,
1088 force=True)
1088 force=True)
1089 if n is None:
1089 if n is None:
1090 raise util.Abort(_("repo commit failed"))
1090 raise util.Abort(_("repo commit failed"))
1091 try:
1091 try:
1092 self.fullseries[insert:insert] = [patchfn]
1092 self.fullseries[insert:insert] = [patchfn]
1093 self.applied.append(statusentry(n, patchfn))
1093 self.applied.append(statusentry(n, patchfn))
1094 self.parseseries()
1094 self.parseseries()
1095 self.seriesdirty = True
1095 self.seriesdirty = True
1096 self.applieddirty = True
1096 self.applieddirty = True
1097 if msg:
1097 if msg:
1098 msg = msg + "\n\n"
1098 msg = msg + "\n\n"
1099 p.write(msg)
1099 p.write(msg)
1100 if commitfiles:
1100 if commitfiles:
1101 parent = self.qparents(repo, n)
1101 parent = self.qparents(repo, n)
1102 if inclsubs:
1102 if inclsubs:
1103 self.putsubstate2changes(substatestate, changes)
1103 self.putsubstate2changes(substatestate, changes)
1104 chunks = patchmod.diff(repo, node1=parent, node2=n,
1104 chunks = patchmod.diff(repo, node1=parent, node2=n,
1105 changes=changes, opts=diffopts)
1105 changes=changes, opts=diffopts)
1106 for chunk in chunks:
1106 for chunk in chunks:
1107 p.write(chunk)
1107 p.write(chunk)
1108 p.close()
1108 p.close()
1109 r = self.qrepo()
1109 r = self.qrepo()
1110 if r:
1110 if r:
1111 r[None].add([patchfn])
1111 r[None].add([patchfn])
1112 except: # re-raises
1112 except: # re-raises
1113 repo.rollback()
1113 repo.rollback()
1114 raise
1114 raise
1115 except Exception:
1115 except Exception:
1116 patchpath = self.join(patchfn)
1116 patchpath = self.join(patchfn)
1117 try:
1117 try:
1118 os.unlink(patchpath)
1118 os.unlink(patchpath)
1119 except OSError:
1119 except OSError:
1120 self.ui.warn(_('error unlinking %s\n') % patchpath)
1120 self.ui.warn(_('error unlinking %s\n') % patchpath)
1121 raise
1121 raise
1122 self.removeundo(repo)
1122 self.removeundo(repo)
1123 finally:
1123 finally:
1124 release(wlock)
1124 release(wlock)
1125
1125
1126 def isapplied(self, patch):
1126 def isapplied(self, patch):
1127 """returns (index, rev, patch)"""
1127 """returns (index, rev, patch)"""
1128 for i, a in enumerate(self.applied):
1128 for i, a in enumerate(self.applied):
1129 if a.name == patch:
1129 if a.name == patch:
1130 return (i, a.node, a.name)
1130 return (i, a.node, a.name)
1131 return None
1131 return None
1132
1132
1133 # if the exact patch name does not exist, we try a few
1133 # if the exact patch name does not exist, we try a few
1134 # variations. If strict is passed, we try only #1
1134 # variations. If strict is passed, we try only #1
1135 #
1135 #
1136 # 1) a number (as string) to indicate an offset in the series file
1136 # 1) a number (as string) to indicate an offset in the series file
1137 # 2) a unique substring of the patch name was given
1137 # 2) a unique substring of the patch name was given
1138 # 3) patchname[-+]num to indicate an offset in the series file
1138 # 3) patchname[-+]num to indicate an offset in the series file
1139 def lookup(self, patch, strict=False):
1139 def lookup(self, patch, strict=False):
1140 def partialname(s):
1140 def partialname(s):
1141 if s in self.series:
1141 if s in self.series:
1142 return s
1142 return s
1143 matches = [x for x in self.series if s in x]
1143 matches = [x for x in self.series if s in x]
1144 if len(matches) > 1:
1144 if len(matches) > 1:
1145 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1145 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1146 for m in matches:
1146 for m in matches:
1147 self.ui.warn(' %s\n' % m)
1147 self.ui.warn(' %s\n' % m)
1148 return None
1148 return None
1149 if matches:
1149 if matches:
1150 return matches[0]
1150 return matches[0]
1151 if self.series and self.applied:
1151 if self.series and self.applied:
1152 if s == 'qtip':
1152 if s == 'qtip':
1153 return self.series[self.seriesend(True) - 1]
1153 return self.series[self.seriesend(True) - 1]
1154 if s == 'qbase':
1154 if s == 'qbase':
1155 return self.series[0]
1155 return self.series[0]
1156 return None
1156 return None
1157
1157
1158 if patch in self.series:
1158 if patch in self.series:
1159 return patch
1159 return patch
1160
1160
1161 if not os.path.isfile(self.join(patch)):
1161 if not os.path.isfile(self.join(patch)):
1162 try:
1162 try:
1163 sno = int(patch)
1163 sno = int(patch)
1164 except (ValueError, OverflowError):
1164 except (ValueError, OverflowError):
1165 pass
1165 pass
1166 else:
1166 else:
1167 if -len(self.series) <= sno < len(self.series):
1167 if -len(self.series) <= sno < len(self.series):
1168 return self.series[sno]
1168 return self.series[sno]
1169
1169
1170 if not strict:
1170 if not strict:
1171 res = partialname(patch)
1171 res = partialname(patch)
1172 if res:
1172 if res:
1173 return res
1173 return res
1174 minus = patch.rfind('-')
1174 minus = patch.rfind('-')
1175 if minus >= 0:
1175 if minus >= 0:
1176 res = partialname(patch[:minus])
1176 res = partialname(patch[:minus])
1177 if res:
1177 if res:
1178 i = self.series.index(res)
1178 i = self.series.index(res)
1179 try:
1179 try:
1180 off = int(patch[minus + 1:] or 1)
1180 off = int(patch[minus + 1:] or 1)
1181 except (ValueError, OverflowError):
1181 except (ValueError, OverflowError):
1182 pass
1182 pass
1183 else:
1183 else:
1184 if i - off >= 0:
1184 if i - off >= 0:
1185 return self.series[i - off]
1185 return self.series[i - off]
1186 plus = patch.rfind('+')
1186 plus = patch.rfind('+')
1187 if plus >= 0:
1187 if plus >= 0:
1188 res = partialname(patch[:plus])
1188 res = partialname(patch[:plus])
1189 if res:
1189 if res:
1190 i = self.series.index(res)
1190 i = self.series.index(res)
1191 try:
1191 try:
1192 off = int(patch[plus + 1:] or 1)
1192 off = int(patch[plus + 1:] or 1)
1193 except (ValueError, OverflowError):
1193 except (ValueError, OverflowError):
1194 pass
1194 pass
1195 else:
1195 else:
1196 if i + off < len(self.series):
1196 if i + off < len(self.series):
1197 return self.series[i + off]
1197 return self.series[i + off]
1198 raise util.Abort(_("patch %s not in series") % patch)
1198 raise util.Abort(_("patch %s not in series") % patch)
1199
1199
1200 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1200 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1201 all=False, move=False, exact=False, nobackup=False,
1201 all=False, move=False, exact=False, nobackup=False,
1202 keepchanges=False):
1202 keepchanges=False):
1203 self.checkkeepchanges(keepchanges, force)
1203 self.checkkeepchanges(keepchanges, force)
1204 diffopts = self.diffopts()
1204 diffopts = self.diffopts()
1205 wlock = repo.wlock()
1205 wlock = repo.wlock()
1206 try:
1206 try:
1207 heads = []
1207 heads = []
1208 for hs in repo.branchmap().itervalues():
1208 for hs in repo.branchmap().itervalues():
1209 heads.extend(hs)
1209 heads.extend(hs)
1210 if not heads:
1210 if not heads:
1211 heads = [nullid]
1211 heads = [nullid]
1212 if repo.dirstate.p1() not in heads and not exact:
1212 if repo.dirstate.p1() not in heads and not exact:
1213 self.ui.status(_("(working directory not at a head)\n"))
1213 self.ui.status(_("(working directory not at a head)\n"))
1214
1214
1215 if not self.series:
1215 if not self.series:
1216 self.ui.warn(_('no patches in series\n'))
1216 self.ui.warn(_('no patches in series\n'))
1217 return 0
1217 return 0
1218
1218
1219 # Suppose our series file is: A B C and the current 'top'
1219 # Suppose our series file is: A B C and the current 'top'
1220 # patch is B. qpush C should be performed (moving forward)
1220 # patch is B. qpush C should be performed (moving forward)
1221 # qpush B is a NOP (no change) qpush A is an error (can't
1221 # qpush B is a NOP (no change) qpush A is an error (can't
1222 # go backwards with qpush)
1222 # go backwards with qpush)
1223 if patch:
1223 if patch:
1224 patch = self.lookup(patch)
1224 patch = self.lookup(patch)
1225 info = self.isapplied(patch)
1225 info = self.isapplied(patch)
1226 if info and info[0] >= len(self.applied) - 1:
1226 if info and info[0] >= len(self.applied) - 1:
1227 self.ui.warn(
1227 self.ui.warn(
1228 _('qpush: %s is already at the top\n') % patch)
1228 _('qpush: %s is already at the top\n') % patch)
1229 return 0
1229 return 0
1230
1230
1231 pushable, reason = self.pushable(patch)
1231 pushable, reason = self.pushable(patch)
1232 if pushable:
1232 if pushable:
1233 if self.series.index(patch) < self.seriesend():
1233 if self.series.index(patch) < self.seriesend():
1234 raise util.Abort(
1234 raise util.Abort(
1235 _("cannot push to a previous patch: %s") % patch)
1235 _("cannot push to a previous patch: %s") % patch)
1236 else:
1236 else:
1237 if reason:
1237 if reason:
1238 reason = _('guarded by %s') % reason
1238 reason = _('guarded by %s') % reason
1239 else:
1239 else:
1240 reason = _('no matching guards')
1240 reason = _('no matching guards')
1241 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1241 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1242 return 1
1242 return 1
1243 elif all:
1243 elif all:
1244 patch = self.series[-1]
1244 patch = self.series[-1]
1245 if self.isapplied(patch):
1245 if self.isapplied(patch):
1246 self.ui.warn(_('all patches are currently applied\n'))
1246 self.ui.warn(_('all patches are currently applied\n'))
1247 return 0
1247 return 0
1248
1248
1249 # Following the above example, starting at 'top' of B:
1249 # Following the above example, starting at 'top' of B:
1250 # qpush should be performed (pushes C), but a subsequent
1250 # qpush should be performed (pushes C), but a subsequent
1251 # qpush without an argument is an error (nothing to
1251 # qpush without an argument is an error (nothing to
1252 # apply). This allows a loop of "...while hg qpush..." to
1252 # apply). This allows a loop of "...while hg qpush..." to
1253 # work as it detects an error when done
1253 # work as it detects an error when done
1254 start = self.seriesend()
1254 start = self.seriesend()
1255 if start == len(self.series):
1255 if start == len(self.series):
1256 self.ui.warn(_('patch series already fully applied\n'))
1256 self.ui.warn(_('patch series already fully applied\n'))
1257 return 1
1257 return 1
1258 if not force and not keepchanges:
1258 if not force and not keepchanges:
1259 self.checklocalchanges(repo, refresh=self.applied)
1259 self.checklocalchanges(repo, refresh=self.applied)
1260
1260
1261 if exact:
1261 if exact:
1262 if keepchanges:
1262 if keepchanges:
1263 raise util.Abort(
1263 raise util.Abort(
1264 _("cannot use --exact and --keep-changes together"))
1264 _("cannot use --exact and --keep-changes together"))
1265 if move:
1265 if move:
1266 raise util.Abort(_('cannot use --exact and --move '
1266 raise util.Abort(_('cannot use --exact and --move '
1267 'together'))
1267 'together'))
1268 if self.applied:
1268 if self.applied:
1269 raise util.Abort(_('cannot push --exact with applied '
1269 raise util.Abort(_('cannot push --exact with applied '
1270 'patches'))
1270 'patches'))
1271 root = self.series[start]
1271 root = self.series[start]
1272 target = patchheader(self.join(root), self.plainmode).parent
1272 target = patchheader(self.join(root), self.plainmode).parent
1273 if not target:
1273 if not target:
1274 raise util.Abort(
1274 raise util.Abort(
1275 _("%s does not have a parent recorded") % root)
1275 _("%s does not have a parent recorded") % root)
1276 if not repo[target] == repo['.']:
1276 if not repo[target] == repo['.']:
1277 hg.update(repo, target)
1277 hg.update(repo, target)
1278
1278
1279 if move:
1279 if move:
1280 if not patch:
1280 if not patch:
1281 raise util.Abort(_("please specify the patch to move"))
1281 raise util.Abort(_("please specify the patch to move"))
1282 for fullstart, rpn in enumerate(self.fullseries):
1282 for fullstart, rpn in enumerate(self.fullseries):
1283 # strip markers for patch guards
1283 # strip markers for patch guards
1284 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1284 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1285 break
1285 break
1286 for i, rpn in enumerate(self.fullseries[fullstart:]):
1286 for i, rpn in enumerate(self.fullseries[fullstart:]):
1287 # strip markers for patch guards
1287 # strip markers for patch guards
1288 if self.guard_re.split(rpn, 1)[0] == patch:
1288 if self.guard_re.split(rpn, 1)[0] == patch:
1289 break
1289 break
1290 index = fullstart + i
1290 index = fullstart + i
1291 assert index < len(self.fullseries)
1291 assert index < len(self.fullseries)
1292 fullpatch = self.fullseries[index]
1292 fullpatch = self.fullseries[index]
1293 del self.fullseries[index]
1293 del self.fullseries[index]
1294 self.fullseries.insert(fullstart, fullpatch)
1294 self.fullseries.insert(fullstart, fullpatch)
1295 self.parseseries()
1295 self.parseseries()
1296 self.seriesdirty = True
1296 self.seriesdirty = True
1297
1297
1298 self.applieddirty = True
1298 self.applieddirty = True
1299 if start > 0:
1299 if start > 0:
1300 self.checktoppatch(repo)
1300 self.checktoppatch(repo)
1301 if not patch:
1301 if not patch:
1302 patch = self.series[start]
1302 patch = self.series[start]
1303 end = start + 1
1303 end = start + 1
1304 else:
1304 else:
1305 end = self.series.index(patch, start) + 1
1305 end = self.series.index(patch, start) + 1
1306
1306
1307 tobackup = set()
1307 tobackup = set()
1308 if (not nobackup and force) or keepchanges:
1308 if (not nobackup and force) or keepchanges:
1309 m, a, r, d = self.checklocalchanges(repo, force=True)
1309 m, a, r, d = self.checklocalchanges(repo, force=True)
1310 if keepchanges:
1310 if keepchanges:
1311 tobackup.update(m + a + r + d)
1311 tobackup.update(m + a + r + d)
1312 else:
1312 else:
1313 tobackup.update(m + a)
1313 tobackup.update(m + a)
1314
1314
1315 s = self.series[start:end]
1315 s = self.series[start:end]
1316 all_files = set()
1316 all_files = set()
1317 try:
1317 try:
1318 if mergeq:
1318 if mergeq:
1319 ret = self.mergepatch(repo, mergeq, s, diffopts)
1319 ret = self.mergepatch(repo, mergeq, s, diffopts)
1320 else:
1320 else:
1321 ret = self.apply(repo, s, list, all_files=all_files,
1321 ret = self.apply(repo, s, list, all_files=all_files,
1322 tobackup=tobackup, keepchanges=keepchanges)
1322 tobackup=tobackup, keepchanges=keepchanges)
1323 except: # re-raises
1323 except: # re-raises
1324 self.ui.warn(_('cleaning up working directory...'))
1324 self.ui.warn(_('cleaning up working directory...'))
1325 node = repo.dirstate.p1()
1325 node = repo.dirstate.p1()
1326 hg.revert(repo, node, None)
1326 hg.revert(repo, node, None)
1327 # only remove unknown files that we know we touched or
1327 # only remove unknown files that we know we touched or
1328 # created while patching
1328 # created while patching
1329 for f in all_files:
1329 for f in all_files:
1330 if f not in repo.dirstate:
1330 if f not in repo.dirstate:
1331 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1331 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1332 self.ui.warn(_('done\n'))
1332 self.ui.warn(_('done\n'))
1333 raise
1333 raise
1334
1334
1335 if not self.applied:
1335 if not self.applied:
1336 return ret[0]
1336 return ret[0]
1337 top = self.applied[-1].name
1337 top = self.applied[-1].name
1338 if ret[0] and ret[0] > 1:
1338 if ret[0] and ret[0] > 1:
1339 msg = _("errors during apply, please fix and refresh %s\n")
1339 msg = _("errors during apply, please fix and refresh %s\n")
1340 self.ui.write(msg % top)
1340 self.ui.write(msg % top)
1341 else:
1341 else:
1342 self.ui.write(_("now at: %s\n") % top)
1342 self.ui.write(_("now at: %s\n") % top)
1343 return ret[0]
1343 return ret[0]
1344
1344
1345 finally:
1345 finally:
1346 wlock.release()
1346 wlock.release()
1347
1347
1348 def pop(self, repo, patch=None, force=False, update=True, all=False,
1348 def pop(self, repo, patch=None, force=False, update=True, all=False,
1349 nobackup=False, keepchanges=False):
1349 nobackup=False, keepchanges=False):
1350 self.checkkeepchanges(keepchanges, force)
1350 self.checkkeepchanges(keepchanges, force)
1351 wlock = repo.wlock()
1351 wlock = repo.wlock()
1352 try:
1352 try:
1353 if patch:
1353 if patch:
1354 # index, rev, patch
1354 # index, rev, patch
1355 info = self.isapplied(patch)
1355 info = self.isapplied(patch)
1356 if not info:
1356 if not info:
1357 patch = self.lookup(patch)
1357 patch = self.lookup(patch)
1358 info = self.isapplied(patch)
1358 info = self.isapplied(patch)
1359 if not info:
1359 if not info:
1360 raise util.Abort(_("patch %s is not applied") % patch)
1360 raise util.Abort(_("patch %s is not applied") % patch)
1361
1361
1362 if not self.applied:
1362 if not self.applied:
1363 # Allow qpop -a to work repeatedly,
1363 # Allow qpop -a to work repeatedly,
1364 # but not qpop without an argument
1364 # but not qpop without an argument
1365 self.ui.warn(_("no patches applied\n"))
1365 self.ui.warn(_("no patches applied\n"))
1366 return not all
1366 return not all
1367
1367
1368 if all:
1368 if all:
1369 start = 0
1369 start = 0
1370 elif patch:
1370 elif patch:
1371 start = info[0] + 1
1371 start = info[0] + 1
1372 else:
1372 else:
1373 start = len(self.applied) - 1
1373 start = len(self.applied) - 1
1374
1374
1375 if start >= len(self.applied):
1375 if start >= len(self.applied):
1376 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1376 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1377 return
1377 return
1378
1378
1379 if not update:
1379 if not update:
1380 parents = repo.dirstate.parents()
1380 parents = repo.dirstate.parents()
1381 rr = [x.node for x in self.applied]
1381 rr = [x.node for x in self.applied]
1382 for p in parents:
1382 for p in parents:
1383 if p in rr:
1383 if p in rr:
1384 self.ui.warn(_("qpop: forcing dirstate update\n"))
1384 self.ui.warn(_("qpop: forcing dirstate update\n"))
1385 update = True
1385 update = True
1386 else:
1386 else:
1387 parents = [p.node() for p in repo[None].parents()]
1387 parents = [p.node() for p in repo[None].parents()]
1388 needupdate = False
1388 needupdate = False
1389 for entry in self.applied[start:]:
1389 for entry in self.applied[start:]:
1390 if entry.node in parents:
1390 if entry.node in parents:
1391 needupdate = True
1391 needupdate = True
1392 break
1392 break
1393 update = needupdate
1393 update = needupdate
1394
1394
1395 tobackup = set()
1395 tobackup = set()
1396 if update:
1396 if update:
1397 m, a, r, d = self.checklocalchanges(
1397 m, a, r, d = self.checklocalchanges(
1398 repo, force=force or keepchanges)
1398 repo, force=force or keepchanges)
1399 if force:
1399 if force:
1400 if not nobackup:
1400 if not nobackup:
1401 tobackup.update(m + a)
1401 tobackup.update(m + a)
1402 elif keepchanges:
1402 elif keepchanges:
1403 tobackup.update(m + a + r + d)
1403 tobackup.update(m + a + r + d)
1404
1404
1405 self.applieddirty = True
1405 self.applieddirty = True
1406 end = len(self.applied)
1406 end = len(self.applied)
1407 rev = self.applied[start].node
1407 rev = self.applied[start].node
1408
1408
1409 try:
1409 try:
1410 heads = repo.changelog.heads(rev)
1410 heads = repo.changelog.heads(rev)
1411 except error.LookupError:
1411 except error.LookupError:
1412 node = short(rev)
1412 node = short(rev)
1413 raise util.Abort(_('trying to pop unknown node %s') % node)
1413 raise util.Abort(_('trying to pop unknown node %s') % node)
1414
1414
1415 if heads != [self.applied[-1].node]:
1415 if heads != [self.applied[-1].node]:
1416 raise util.Abort(_("popping would remove a revision not "
1416 raise util.Abort(_("popping would remove a revision not "
1417 "managed by this patch queue"))
1417 "managed by this patch queue"))
1418 if not repo[self.applied[-1].node].mutable():
1418 if not repo[self.applied[-1].node].mutable():
1419 raise util.Abort(
1419 raise util.Abort(
1420 _("popping would remove an immutable revision"),
1420 _("popping would remove an immutable revision"),
1421 hint=_('see "hg help phases" for details'))
1421 hint=_('see "hg help phases" for details'))
1422
1422
1423 # we know there are no local changes, so we can make a simplified
1423 # we know there are no local changes, so we can make a simplified
1424 # form of hg.update.
1424 # form of hg.update.
1425 if update:
1425 if update:
1426 qp = self.qparents(repo, rev)
1426 qp = self.qparents(repo, rev)
1427 ctx = repo[qp]
1427 ctx = repo[qp]
1428 m, a, r, d = repo.status(qp, '.')[:4]
1428 m, a, r, d = repo.status(qp, '.')[:4]
1429 if d:
1429 if d:
1430 raise util.Abort(_("deletions found between repo revs"))
1430 raise util.Abort(_("deletions found between repo revs"))
1431
1431
1432 tobackup = set(a + m + r) & tobackup
1432 tobackup = set(a + m + r) & tobackup
1433 if keepchanges and tobackup:
1433 if keepchanges and tobackup:
1434 raise util.Abort(_("local changes found, refresh first"))
1434 raise util.Abort(_("local changes found, refresh first"))
1435 self.backup(repo, tobackup)
1435 self.backup(repo, tobackup)
1436
1436
1437 for f in a:
1437 for f in a:
1438 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1438 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1439 repo.dirstate.drop(f)
1439 repo.dirstate.drop(f)
1440 for f in m + r:
1440 for f in m + r:
1441 fctx = ctx[f]
1441 fctx = ctx[f]
1442 repo.wwrite(f, fctx.data(), fctx.flags())
1442 repo.wwrite(f, fctx.data(), fctx.flags())
1443 repo.dirstate.normal(f)
1443 repo.dirstate.normal(f)
1444 repo.setparents(qp, nullid)
1444 repo.setparents(qp, nullid)
1445 for patch in reversed(self.applied[start:end]):
1445 for patch in reversed(self.applied[start:end]):
1446 self.ui.status(_("popping %s\n") % patch.name)
1446 self.ui.status(_("popping %s\n") % patch.name)
1447 del self.applied[start:end]
1447 del self.applied[start:end]
1448 strip(self.ui, repo, [rev], update=False, backup='strip')
1448 strip(self.ui, repo, [rev], update=False, backup='strip')
1449 for s, state in repo['.'].substate.items():
1449 for s, state in repo['.'].substate.items():
1450 repo['.'].sub(s).get(state)
1450 repo['.'].sub(s).get(state)
1451 if self.applied:
1451 if self.applied:
1452 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1452 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1453 else:
1453 else:
1454 self.ui.write(_("patch queue now empty\n"))
1454 self.ui.write(_("patch queue now empty\n"))
1455 finally:
1455 finally:
1456 wlock.release()
1456 wlock.release()
1457
1457
1458 def diff(self, repo, pats, opts):
1458 def diff(self, repo, pats, opts):
1459 top, patch = self.checktoppatch(repo)
1459 top, patch = self.checktoppatch(repo)
1460 if not top:
1460 if not top:
1461 self.ui.write(_("no patches applied\n"))
1461 self.ui.write(_("no patches applied\n"))
1462 return
1462 return
1463 qp = self.qparents(repo, top)
1463 qp = self.qparents(repo, top)
1464 if opts.get('reverse'):
1464 if opts.get('reverse'):
1465 node1, node2 = None, qp
1465 node1, node2 = None, qp
1466 else:
1466 else:
1467 node1, node2 = qp, None
1467 node1, node2 = qp, None
1468 diffopts = self.diffopts(opts, patch)
1468 diffopts = self.diffopts(opts, patch)
1469 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1469 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1470
1470
1471 def refresh(self, repo, pats=None, **opts):
1471 def refresh(self, repo, pats=None, **opts):
1472 if not self.applied:
1472 if not self.applied:
1473 self.ui.write(_("no patches applied\n"))
1473 self.ui.write(_("no patches applied\n"))
1474 return 1
1474 return 1
1475 msg = opts.get('msg', '').rstrip()
1475 msg = opts.get('msg', '').rstrip()
1476 newuser = opts.get('user')
1476 newuser = opts.get('user')
1477 newdate = opts.get('date')
1477 newdate = opts.get('date')
1478 if newdate:
1478 if newdate:
1479 newdate = '%d %d' % util.parsedate(newdate)
1479 newdate = '%d %d' % util.parsedate(newdate)
1480 wlock = repo.wlock()
1480 wlock = repo.wlock()
1481
1481
1482 try:
1482 try:
1483 self.checktoppatch(repo)
1483 self.checktoppatch(repo)
1484 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1484 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1485 if repo.changelog.heads(top) != [top]:
1485 if repo.changelog.heads(top) != [top]:
1486 raise util.Abort(_("cannot refresh a revision with children"))
1486 raise util.Abort(_("cannot refresh a revision with children"))
1487 if not repo[top].mutable():
1487 if not repo[top].mutable():
1488 raise util.Abort(_("cannot refresh immutable revision"),
1488 raise util.Abort(_("cannot refresh immutable revision"),
1489 hint=_('see "hg help phases" for details'))
1489 hint=_('see "hg help phases" for details'))
1490
1490
1491 cparents = repo.changelog.parents(top)
1491 cparents = repo.changelog.parents(top)
1492 patchparent = self.qparents(repo, top)
1492 patchparent = self.qparents(repo, top)
1493
1493
1494 inclsubs = checksubstate(repo, hex(patchparent))
1494 inclsubs = checksubstate(repo, hex(patchparent))
1495 if inclsubs:
1495 if inclsubs:
1496 inclsubs.append('.hgsubstate')
1496 inclsubs.append('.hgsubstate')
1497 substatestate = repo.dirstate['.hgsubstate']
1497 substatestate = repo.dirstate['.hgsubstate']
1498
1498
1499 ph = patchheader(self.join(patchfn), self.plainmode)
1499 ph = patchheader(self.join(patchfn), self.plainmode)
1500 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1500 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1501 if msg:
1501 if msg:
1502 ph.setmessage(msg)
1502 ph.setmessage(msg)
1503 if newuser:
1503 if newuser:
1504 ph.setuser(newuser)
1504 ph.setuser(newuser)
1505 if newdate:
1505 if newdate:
1506 ph.setdate(newdate)
1506 ph.setdate(newdate)
1507 ph.setparent(hex(patchparent))
1507 ph.setparent(hex(patchparent))
1508
1508
1509 # only commit new patch when write is complete
1509 # only commit new patch when write is complete
1510 patchf = self.opener(patchfn, 'w', atomictemp=True)
1510 patchf = self.opener(patchfn, 'w', atomictemp=True)
1511
1511
1512 comments = str(ph)
1512 comments = str(ph)
1513 if comments:
1513 if comments:
1514 patchf.write(comments)
1514 patchf.write(comments)
1515
1515
1516 # update the dirstate in place, strip off the qtip commit
1516 # update the dirstate in place, strip off the qtip commit
1517 # and then commit.
1517 # and then commit.
1518 #
1518 #
1519 # this should really read:
1519 # this should really read:
1520 # mm, dd, aa = repo.status(top, patchparent)[:3]
1520 # mm, dd, aa = repo.status(top, patchparent)[:3]
1521 # but we do it backwards to take advantage of manifest/changelog
1521 # but we do it backwards to take advantage of manifest/changelog
1522 # caching against the next repo.status call
1522 # caching against the next repo.status call
1523 mm, aa, dd = repo.status(patchparent, top)[:3]
1523 mm, aa, dd = repo.status(patchparent, top)[:3]
1524 changes = repo.changelog.read(top)
1524 changes = repo.changelog.read(top)
1525 man = repo.manifest.read(changes[0])
1525 man = repo.manifest.read(changes[0])
1526 aaa = aa[:]
1526 aaa = aa[:]
1527 matchfn = scmutil.match(repo[None], pats, opts)
1527 matchfn = scmutil.match(repo[None], pats, opts)
1528 # in short mode, we only diff the files included in the
1528 # in short mode, we only diff the files included in the
1529 # patch already plus specified files
1529 # patch already plus specified files
1530 if opts.get('short'):
1530 if opts.get('short'):
1531 # if amending a patch, we start with existing
1531 # if amending a patch, we start with existing
1532 # files plus specified files - unfiltered
1532 # files plus specified files - unfiltered
1533 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1533 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1534 # filter with include/exclude options
1534 # filter with include/exclude options
1535 matchfn = scmutil.match(repo[None], opts=opts)
1535 matchfn = scmutil.match(repo[None], opts=opts)
1536 else:
1536 else:
1537 match = scmutil.matchall(repo)
1537 match = scmutil.matchall(repo)
1538 m, a, r, d = repo.status(match=match)[:4]
1538 m, a, r, d = repo.status(match=match)[:4]
1539 mm = set(mm)
1539 mm = set(mm)
1540 aa = set(aa)
1540 aa = set(aa)
1541 dd = set(dd)
1541 dd = set(dd)
1542
1542
1543 # we might end up with files that were added between
1543 # we might end up with files that were added between
1544 # qtip and the dirstate parent, but then changed in the
1544 # qtip and the dirstate parent, but then changed in the
1545 # local dirstate. in this case, we want them to only
1545 # local dirstate. in this case, we want them to only
1546 # show up in the added section
1546 # show up in the added section
1547 for x in m:
1547 for x in m:
1548 if x not in aa:
1548 if x not in aa:
1549 mm.add(x)
1549 mm.add(x)
1550 # we might end up with files added by the local dirstate that
1550 # we might end up with files added by the local dirstate that
1551 # were deleted by the patch. In this case, they should only
1551 # were deleted by the patch. In this case, they should only
1552 # show up in the changed section.
1552 # show up in the changed section.
1553 for x in a:
1553 for x in a:
1554 if x in dd:
1554 if x in dd:
1555 dd.remove(x)
1555 dd.remove(x)
1556 mm.add(x)
1556 mm.add(x)
1557 else:
1557 else:
1558 aa.add(x)
1558 aa.add(x)
1559 # make sure any files deleted in the local dirstate
1559 # make sure any files deleted in the local dirstate
1560 # are not in the add or change column of the patch
1560 # are not in the add or change column of the patch
1561 forget = []
1561 forget = []
1562 for x in d + r:
1562 for x in d + r:
1563 if x in aa:
1563 if x in aa:
1564 aa.remove(x)
1564 aa.remove(x)
1565 forget.append(x)
1565 forget.append(x)
1566 continue
1566 continue
1567 else:
1567 else:
1568 mm.discard(x)
1568 mm.discard(x)
1569 dd.add(x)
1569 dd.add(x)
1570
1570
1571 m = list(mm)
1571 m = list(mm)
1572 r = list(dd)
1572 r = list(dd)
1573 a = list(aa)
1573 a = list(aa)
1574
1574
1575 # create 'match' that includes the files to be recommitted.
1575 # create 'match' that includes the files to be recommitted.
1576 # apply matchfn via repo.status to ensure correct case handling.
1576 # apply matchfn via repo.status to ensure correct case handling.
1577 cm, ca, cr, cd = repo.status(patchparent, match=matchfn)[:4]
1577 cm, ca, cr, cd = repo.status(patchparent, match=matchfn)[:4]
1578 allmatches = set(cm + ca + cr + cd)
1578 allmatches = set(cm + ca + cr + cd)
1579 refreshchanges = [x.intersection(allmatches) for x in (mm, aa, dd)]
1579 refreshchanges = [x.intersection(allmatches) for x in (mm, aa, dd)]
1580
1580
1581 files = set(inclsubs)
1581 files = set(inclsubs)
1582 for x in refreshchanges:
1582 for x in refreshchanges:
1583 files.update(x)
1583 files.update(x)
1584 match = scmutil.matchfiles(repo, files)
1584 match = scmutil.matchfiles(repo, files)
1585
1585
1586 bmlist = repo[top].bookmarks()
1586 bmlist = repo[top].bookmarks()
1587
1587
1588 try:
1588 try:
1589 if diffopts.git or diffopts.upgrade:
1589 if diffopts.git or diffopts.upgrade:
1590 copies = {}
1590 copies = {}
1591 for dst in a:
1591 for dst in a:
1592 src = repo.dirstate.copied(dst)
1592 src = repo.dirstate.copied(dst)
1593 # during qfold, the source file for copies may
1593 # during qfold, the source file for copies may
1594 # be removed. Treat this as a simple add.
1594 # be removed. Treat this as a simple add.
1595 if src is not None and src in repo.dirstate:
1595 if src is not None and src in repo.dirstate:
1596 copies.setdefault(src, []).append(dst)
1596 copies.setdefault(src, []).append(dst)
1597 repo.dirstate.add(dst)
1597 repo.dirstate.add(dst)
1598 # remember the copies between patchparent and qtip
1598 # remember the copies between patchparent and qtip
1599 for dst in aaa:
1599 for dst in aaa:
1600 f = repo.file(dst)
1600 f = repo.file(dst)
1601 src = f.renamed(man[dst])
1601 src = f.renamed(man[dst])
1602 if src:
1602 if src:
1603 copies.setdefault(src[0], []).extend(
1603 copies.setdefault(src[0], []).extend(
1604 copies.get(dst, []))
1604 copies.get(dst, []))
1605 if dst in a:
1605 if dst in a:
1606 copies[src[0]].append(dst)
1606 copies[src[0]].append(dst)
1607 # we can't copy a file created by the patch itself
1607 # we can't copy a file created by the patch itself
1608 if dst in copies:
1608 if dst in copies:
1609 del copies[dst]
1609 del copies[dst]
1610 for src, dsts in copies.iteritems():
1610 for src, dsts in copies.iteritems():
1611 for dst in dsts:
1611 for dst in dsts:
1612 repo.dirstate.copy(src, dst)
1612 repo.dirstate.copy(src, dst)
1613 else:
1613 else:
1614 for dst in a:
1614 for dst in a:
1615 repo.dirstate.add(dst)
1615 repo.dirstate.add(dst)
1616 # Drop useless copy information
1616 # Drop useless copy information
1617 for f in list(repo.dirstate.copies()):
1617 for f in list(repo.dirstate.copies()):
1618 repo.dirstate.copy(None, f)
1618 repo.dirstate.copy(None, f)
1619 for f in r:
1619 for f in r:
1620 repo.dirstate.remove(f)
1620 repo.dirstate.remove(f)
1621 # if the patch excludes a modified file, mark that
1621 # if the patch excludes a modified file, mark that
1622 # file with mtime=0 so status can see it.
1622 # file with mtime=0 so status can see it.
1623 mm = []
1623 mm = []
1624 for i in xrange(len(m) - 1, -1, -1):
1624 for i in xrange(len(m) - 1, -1, -1):
1625 if not matchfn(m[i]):
1625 if not matchfn(m[i]):
1626 mm.append(m[i])
1626 mm.append(m[i])
1627 del m[i]
1627 del m[i]
1628 for f in m:
1628 for f in m:
1629 repo.dirstate.normal(f)
1629 repo.dirstate.normal(f)
1630 for f in mm:
1630 for f in mm:
1631 repo.dirstate.normallookup(f)
1631 repo.dirstate.normallookup(f)
1632 for f in forget:
1632 for f in forget:
1633 repo.dirstate.drop(f)
1633 repo.dirstate.drop(f)
1634
1634
1635 if not msg:
1635 if not msg:
1636 if not ph.message:
1636 if not ph.message:
1637 message = "[mq]: %s\n" % patchfn
1637 message = "[mq]: %s\n" % patchfn
1638 else:
1638 else:
1639 message = "\n".join(ph.message)
1639 message = "\n".join(ph.message)
1640 else:
1640 else:
1641 message = msg
1641 message = msg
1642
1642
1643 user = ph.user or changes[1]
1643 user = ph.user or changes[1]
1644
1644
1645 oldphase = repo[top].phase()
1645 oldphase = repo[top].phase()
1646
1646
1647 # assumes strip can roll itself back if interrupted
1647 # assumes strip can roll itself back if interrupted
1648 repo.setparents(*cparents)
1648 repo.setparents(*cparents)
1649 self.applied.pop()
1649 self.applied.pop()
1650 self.applieddirty = True
1650 self.applieddirty = True
1651 strip(self.ui, repo, [top], update=False, backup='strip')
1651 strip(self.ui, repo, [top], update=False, backup='strip')
1652 except: # re-raises
1652 except: # re-raises
1653 repo.dirstate.invalidate()
1653 repo.dirstate.invalidate()
1654 raise
1654 raise
1655
1655
1656 try:
1656 try:
1657 # might be nice to attempt to roll back strip after this
1657 # might be nice to attempt to roll back strip after this
1658
1658
1659 # Ensure we create a new changeset in the same phase than
1659 # Ensure we create a new changeset in the same phase than
1660 # the old one.
1660 # the old one.
1661 n = newcommit(repo, oldphase, message, user, ph.date,
1661 n = newcommit(repo, oldphase, message, user, ph.date,
1662 match=match, force=True)
1662 match=match, force=True)
1663 # only write patch after a successful commit
1663 # only write patch after a successful commit
1664 c = [list(x) for x in refreshchanges]
1664 c = [list(x) for x in refreshchanges]
1665 if inclsubs:
1665 if inclsubs:
1666 self.putsubstate2changes(substatestate, c)
1666 self.putsubstate2changes(substatestate, c)
1667 chunks = patchmod.diff(repo, patchparent,
1667 chunks = patchmod.diff(repo, patchparent,
1668 changes=c, opts=diffopts)
1668 changes=c, opts=diffopts)
1669 for chunk in chunks:
1669 for chunk in chunks:
1670 patchf.write(chunk)
1670 patchf.write(chunk)
1671 patchf.close()
1671 patchf.close()
1672
1672
1673 marks = repo._bookmarks
1673 marks = repo._bookmarks
1674 for bm in bmlist:
1674 for bm in bmlist:
1675 marks[bm] = n
1675 marks[bm] = n
1676 marks.write()
1676 marks.write()
1677
1677
1678 self.applied.append(statusentry(n, patchfn))
1678 self.applied.append(statusentry(n, patchfn))
1679 except: # re-raises
1679 except: # re-raises
1680 ctx = repo[cparents[0]]
1680 ctx = repo[cparents[0]]
1681 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1681 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1682 self.savedirty()
1682 self.savedirty()
1683 self.ui.warn(_('refresh interrupted while patch was popped! '
1683 self.ui.warn(_('refresh interrupted while patch was popped! '
1684 '(revert --all, qpush to recover)\n'))
1684 '(revert --all, qpush to recover)\n'))
1685 raise
1685 raise
1686 finally:
1686 finally:
1687 wlock.release()
1687 wlock.release()
1688 self.removeundo(repo)
1688 self.removeundo(repo)
1689
1689
1690 def init(self, repo, create=False):
1690 def init(self, repo, create=False):
1691 if not create and os.path.isdir(self.path):
1691 if not create and os.path.isdir(self.path):
1692 raise util.Abort(_("patch queue directory already exists"))
1692 raise util.Abort(_("patch queue directory already exists"))
1693 try:
1693 try:
1694 os.mkdir(self.path)
1694 os.mkdir(self.path)
1695 except OSError, inst:
1695 except OSError, inst:
1696 if inst.errno != errno.EEXIST or not create:
1696 if inst.errno != errno.EEXIST or not create:
1697 raise
1697 raise
1698 if create:
1698 if create:
1699 return self.qrepo(create=True)
1699 return self.qrepo(create=True)
1700
1700
1701 def unapplied(self, repo, patch=None):
1701 def unapplied(self, repo, patch=None):
1702 if patch and patch not in self.series:
1702 if patch and patch not in self.series:
1703 raise util.Abort(_("patch %s is not in series file") % patch)
1703 raise util.Abort(_("patch %s is not in series file") % patch)
1704 if not patch:
1704 if not patch:
1705 start = self.seriesend()
1705 start = self.seriesend()
1706 else:
1706 else:
1707 start = self.series.index(patch) + 1
1707 start = self.series.index(patch) + 1
1708 unapplied = []
1708 unapplied = []
1709 for i in xrange(start, len(self.series)):
1709 for i in xrange(start, len(self.series)):
1710 pushable, reason = self.pushable(i)
1710 pushable, reason = self.pushable(i)
1711 if pushable:
1711 if pushable:
1712 unapplied.append((i, self.series[i]))
1712 unapplied.append((i, self.series[i]))
1713 self.explainpushable(i)
1713 self.explainpushable(i)
1714 return unapplied
1714 return unapplied
1715
1715
1716 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1716 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1717 summary=False):
1717 summary=False):
1718 def displayname(pfx, patchname, state):
1718 def displayname(pfx, patchname, state):
1719 if pfx:
1719 if pfx:
1720 self.ui.write(pfx)
1720 self.ui.write(pfx)
1721 if summary:
1721 if summary:
1722 ph = patchheader(self.join(patchname), self.plainmode)
1722 ph = patchheader(self.join(patchname), self.plainmode)
1723 msg = ph.message and ph.message[0] or ''
1723 msg = ph.message and ph.message[0] or ''
1724 if self.ui.formatted():
1724 if self.ui.formatted():
1725 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1725 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1726 if width > 0:
1726 if width > 0:
1727 msg = util.ellipsis(msg, width)
1727 msg = util.ellipsis(msg, width)
1728 else:
1728 else:
1729 msg = ''
1729 msg = ''
1730 self.ui.write(patchname, label='qseries.' + state)
1730 self.ui.write(patchname, label='qseries.' + state)
1731 self.ui.write(': ')
1731 self.ui.write(': ')
1732 self.ui.write(msg, label='qseries.message.' + state)
1732 self.ui.write(msg, label='qseries.message.' + state)
1733 else:
1733 else:
1734 self.ui.write(patchname, label='qseries.' + state)
1734 self.ui.write(patchname, label='qseries.' + state)
1735 self.ui.write('\n')
1735 self.ui.write('\n')
1736
1736
1737 applied = set([p.name for p in self.applied])
1737 applied = set([p.name for p in self.applied])
1738 if length is None:
1738 if length is None:
1739 length = len(self.series) - start
1739 length = len(self.series) - start
1740 if not missing:
1740 if not missing:
1741 if self.ui.verbose:
1741 if self.ui.verbose:
1742 idxwidth = len(str(start + length - 1))
1742 idxwidth = len(str(start + length - 1))
1743 for i in xrange(start, start + length):
1743 for i in xrange(start, start + length):
1744 patch = self.series[i]
1744 patch = self.series[i]
1745 if patch in applied:
1745 if patch in applied:
1746 char, state = 'A', 'applied'
1746 char, state = 'A', 'applied'
1747 elif self.pushable(i)[0]:
1747 elif self.pushable(i)[0]:
1748 char, state = 'U', 'unapplied'
1748 char, state = 'U', 'unapplied'
1749 else:
1749 else:
1750 char, state = 'G', 'guarded'
1750 char, state = 'G', 'guarded'
1751 pfx = ''
1751 pfx = ''
1752 if self.ui.verbose:
1752 if self.ui.verbose:
1753 pfx = '%*d %s ' % (idxwidth, i, char)
1753 pfx = '%*d %s ' % (idxwidth, i, char)
1754 elif status and status != char:
1754 elif status and status != char:
1755 continue
1755 continue
1756 displayname(pfx, patch, state)
1756 displayname(pfx, patch, state)
1757 else:
1757 else:
1758 msng_list = []
1758 msng_list = []
1759 for root, dirs, files in os.walk(self.path):
1759 for root, dirs, files in os.walk(self.path):
1760 d = root[len(self.path) + 1:]
1760 d = root[len(self.path) + 1:]
1761 for f in files:
1761 for f in files:
1762 fl = os.path.join(d, f)
1762 fl = os.path.join(d, f)
1763 if (fl not in self.series and
1763 if (fl not in self.series and
1764 fl not in (self.statuspath, self.seriespath,
1764 fl not in (self.statuspath, self.seriespath,
1765 self.guardspath)
1765 self.guardspath)
1766 and not fl.startswith('.')):
1766 and not fl.startswith('.')):
1767 msng_list.append(fl)
1767 msng_list.append(fl)
1768 for x in sorted(msng_list):
1768 for x in sorted(msng_list):
1769 pfx = self.ui.verbose and ('D ') or ''
1769 pfx = self.ui.verbose and ('D ') or ''
1770 displayname(pfx, x, 'missing')
1770 displayname(pfx, x, 'missing')
1771
1771
1772 def issaveline(self, l):
1772 def issaveline(self, l):
1773 if l.name == '.hg.patches.save.line':
1773 if l.name == '.hg.patches.save.line':
1774 return True
1774 return True
1775
1775
1776 def qrepo(self, create=False):
1776 def qrepo(self, create=False):
1777 ui = self.baseui.copy()
1777 ui = self.baseui.copy()
1778 if create or os.path.isdir(self.join(".hg")):
1778 if create or os.path.isdir(self.join(".hg")):
1779 return hg.repository(ui, path=self.path, create=create)
1779 return hg.repository(ui, path=self.path, create=create)
1780
1780
1781 def restore(self, repo, rev, delete=None, qupdate=None):
1781 def restore(self, repo, rev, delete=None, qupdate=None):
1782 desc = repo[rev].description().strip()
1782 desc = repo[rev].description().strip()
1783 lines = desc.splitlines()
1783 lines = desc.splitlines()
1784 i = 0
1784 i = 0
1785 datastart = None
1785 datastart = None
1786 series = []
1786 series = []
1787 applied = []
1787 applied = []
1788 qpp = None
1788 qpp = None
1789 for i, line in enumerate(lines):
1789 for i, line in enumerate(lines):
1790 if line == 'Patch Data:':
1790 if line == 'Patch Data:':
1791 datastart = i + 1
1791 datastart = i + 1
1792 elif line.startswith('Dirstate:'):
1792 elif line.startswith('Dirstate:'):
1793 l = line.rstrip()
1793 l = line.rstrip()
1794 l = l[10:].split(' ')
1794 l = l[10:].split(' ')
1795 qpp = [bin(x) for x in l]
1795 qpp = [bin(x) for x in l]
1796 elif datastart is not None:
1796 elif datastart is not None:
1797 l = line.rstrip()
1797 l = line.rstrip()
1798 n, name = l.split(':', 1)
1798 n, name = l.split(':', 1)
1799 if n:
1799 if n:
1800 applied.append(statusentry(bin(n), name))
1800 applied.append(statusentry(bin(n), name))
1801 else:
1801 else:
1802 series.append(l)
1802 series.append(l)
1803 if datastart is None:
1803 if datastart is None:
1804 self.ui.warn(_("no saved patch data found\n"))
1804 self.ui.warn(_("no saved patch data found\n"))
1805 return 1
1805 return 1
1806 self.ui.warn(_("restoring status: %s\n") % lines[0])
1806 self.ui.warn(_("restoring status: %s\n") % lines[0])
1807 self.fullseries = series
1807 self.fullseries = series
1808 self.applied = applied
1808 self.applied = applied
1809 self.parseseries()
1809 self.parseseries()
1810 self.seriesdirty = True
1810 self.seriesdirty = True
1811 self.applieddirty = True
1811 self.applieddirty = True
1812 heads = repo.changelog.heads()
1812 heads = repo.changelog.heads()
1813 if delete:
1813 if delete:
1814 if rev not in heads:
1814 if rev not in heads:
1815 self.ui.warn(_("save entry has children, leaving it alone\n"))
1815 self.ui.warn(_("save entry has children, leaving it alone\n"))
1816 else:
1816 else:
1817 self.ui.warn(_("removing save entry %s\n") % short(rev))
1817 self.ui.warn(_("removing save entry %s\n") % short(rev))
1818 pp = repo.dirstate.parents()
1818 pp = repo.dirstate.parents()
1819 if rev in pp:
1819 if rev in pp:
1820 update = True
1820 update = True
1821 else:
1821 else:
1822 update = False
1822 update = False
1823 strip(self.ui, repo, [rev], update=update, backup='strip')
1823 strip(self.ui, repo, [rev], update=update, backup='strip')
1824 if qpp:
1824 if qpp:
1825 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1825 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1826 (short(qpp[0]), short(qpp[1])))
1826 (short(qpp[0]), short(qpp[1])))
1827 if qupdate:
1827 if qupdate:
1828 self.ui.status(_("updating queue directory\n"))
1828 self.ui.status(_("updating queue directory\n"))
1829 r = self.qrepo()
1829 r = self.qrepo()
1830 if not r:
1830 if not r:
1831 self.ui.warn(_("unable to load queue repository\n"))
1831 self.ui.warn(_("unable to load queue repository\n"))
1832 return 1
1832 return 1
1833 hg.clean(r, qpp[0])
1833 hg.clean(r, qpp[0])
1834
1834
1835 def save(self, repo, msg=None):
1835 def save(self, repo, msg=None):
1836 if not self.applied:
1836 if not self.applied:
1837 self.ui.warn(_("save: no patches applied, exiting\n"))
1837 self.ui.warn(_("save: no patches applied, exiting\n"))
1838 return 1
1838 return 1
1839 if self.issaveline(self.applied[-1]):
1839 if self.issaveline(self.applied[-1]):
1840 self.ui.warn(_("status is already saved\n"))
1840 self.ui.warn(_("status is already saved\n"))
1841 return 1
1841 return 1
1842
1842
1843 if not msg:
1843 if not msg:
1844 msg = _("hg patches saved state")
1844 msg = _("hg patches saved state")
1845 else:
1845 else:
1846 msg = "hg patches: " + msg.rstrip('\r\n')
1846 msg = "hg patches: " + msg.rstrip('\r\n')
1847 r = self.qrepo()
1847 r = self.qrepo()
1848 if r:
1848 if r:
1849 pp = r.dirstate.parents()
1849 pp = r.dirstate.parents()
1850 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1850 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1851 msg += "\n\nPatch Data:\n"
1851 msg += "\n\nPatch Data:\n"
1852 msg += ''.join('%s\n' % x for x in self.applied)
1852 msg += ''.join('%s\n' % x for x in self.applied)
1853 msg += ''.join(':%s\n' % x for x in self.fullseries)
1853 msg += ''.join(':%s\n' % x for x in self.fullseries)
1854 n = repo.commit(msg, force=True)
1854 n = repo.commit(msg, force=True)
1855 if not n:
1855 if not n:
1856 self.ui.warn(_("repo commit failed\n"))
1856 self.ui.warn(_("repo commit failed\n"))
1857 return 1
1857 return 1
1858 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1858 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1859 self.applieddirty = True
1859 self.applieddirty = True
1860 self.removeundo(repo)
1860 self.removeundo(repo)
1861
1861
1862 def fullseriesend(self):
1862 def fullseriesend(self):
1863 if self.applied:
1863 if self.applied:
1864 p = self.applied[-1].name
1864 p = self.applied[-1].name
1865 end = self.findseries(p)
1865 end = self.findseries(p)
1866 if end is None:
1866 if end is None:
1867 return len(self.fullseries)
1867 return len(self.fullseries)
1868 return end + 1
1868 return end + 1
1869 return 0
1869 return 0
1870
1870
1871 def seriesend(self, all_patches=False):
1871 def seriesend(self, all_patches=False):
1872 """If all_patches is False, return the index of the next pushable patch
1872 """If all_patches is False, return the index of the next pushable patch
1873 in the series, or the series length. If all_patches is True, return the
1873 in the series, or the series length. If all_patches is True, return the
1874 index of the first patch past the last applied one.
1874 index of the first patch past the last applied one.
1875 """
1875 """
1876 end = 0
1876 end = 0
1877 def nextpatch(start):
1877 def nextpatch(start):
1878 if all_patches or start >= len(self.series):
1878 if all_patches or start >= len(self.series):
1879 return start
1879 return start
1880 for i in xrange(start, len(self.series)):
1880 for i in xrange(start, len(self.series)):
1881 p, reason = self.pushable(i)
1881 p, reason = self.pushable(i)
1882 if p:
1882 if p:
1883 return i
1883 return i
1884 self.explainpushable(i)
1884 self.explainpushable(i)
1885 return len(self.series)
1885 return len(self.series)
1886 if self.applied:
1886 if self.applied:
1887 p = self.applied[-1].name
1887 p = self.applied[-1].name
1888 try:
1888 try:
1889 end = self.series.index(p)
1889 end = self.series.index(p)
1890 except ValueError:
1890 except ValueError:
1891 return 0
1891 return 0
1892 return nextpatch(end + 1)
1892 return nextpatch(end + 1)
1893 return nextpatch(end)
1893 return nextpatch(end)
1894
1894
1895 def appliedname(self, index):
1895 def appliedname(self, index):
1896 pname = self.applied[index].name
1896 pname = self.applied[index].name
1897 if not self.ui.verbose:
1897 if not self.ui.verbose:
1898 p = pname
1898 p = pname
1899 else:
1899 else:
1900 p = str(self.series.index(pname)) + " " + pname
1900 p = str(self.series.index(pname)) + " " + pname
1901 return p
1901 return p
1902
1902
1903 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1903 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1904 force=None, git=False):
1904 force=None, git=False):
1905 def checkseries(patchname):
1905 def checkseries(patchname):
1906 if patchname in self.series:
1906 if patchname in self.series:
1907 raise util.Abort(_('patch %s is already in the series file')
1907 raise util.Abort(_('patch %s is already in the series file')
1908 % patchname)
1908 % patchname)
1909
1909
1910 if rev:
1910 if rev:
1911 if files:
1911 if files:
1912 raise util.Abort(_('option "-r" not valid when importing '
1912 raise util.Abort(_('option "-r" not valid when importing '
1913 'files'))
1913 'files'))
1914 rev = scmutil.revrange(repo, rev)
1914 rev = scmutil.revrange(repo, rev)
1915 rev.sort(reverse=True)
1915 rev.sort(reverse=True)
1916 elif not files:
1916 elif not files:
1917 raise util.Abort(_('no files or revisions specified'))
1917 raise util.Abort(_('no files or revisions specified'))
1918 if (len(files) > 1 or len(rev) > 1) and patchname:
1918 if (len(files) > 1 or len(rev) > 1) and patchname:
1919 raise util.Abort(_('option "-n" not valid when importing multiple '
1919 raise util.Abort(_('option "-n" not valid when importing multiple '
1920 'patches'))
1920 'patches'))
1921 imported = []
1921 imported = []
1922 if rev:
1922 if rev:
1923 # If mq patches are applied, we can only import revisions
1923 # If mq patches are applied, we can only import revisions
1924 # that form a linear path to qbase.
1924 # that form a linear path to qbase.
1925 # Otherwise, they should form a linear path to a head.
1925 # Otherwise, they should form a linear path to a head.
1926 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1926 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1927 if len(heads) > 1:
1927 if len(heads) > 1:
1928 raise util.Abort(_('revision %d is the root of more than one '
1928 raise util.Abort(_('revision %d is the root of more than one '
1929 'branch') % rev[-1])
1929 'branch') % rev[-1])
1930 if self.applied:
1930 if self.applied:
1931 base = repo.changelog.node(rev[0])
1931 base = repo.changelog.node(rev[0])
1932 if base in [n.node for n in self.applied]:
1932 if base in [n.node for n in self.applied]:
1933 raise util.Abort(_('revision %d is already managed')
1933 raise util.Abort(_('revision %d is already managed')
1934 % rev[0])
1934 % rev[0])
1935 if heads != [self.applied[-1].node]:
1935 if heads != [self.applied[-1].node]:
1936 raise util.Abort(_('revision %d is not the parent of '
1936 raise util.Abort(_('revision %d is not the parent of '
1937 'the queue') % rev[0])
1937 'the queue') % rev[0])
1938 base = repo.changelog.rev(self.applied[0].node)
1938 base = repo.changelog.rev(self.applied[0].node)
1939 lastparent = repo.changelog.parentrevs(base)[0]
1939 lastparent = repo.changelog.parentrevs(base)[0]
1940 else:
1940 else:
1941 if heads != [repo.changelog.node(rev[0])]:
1941 if heads != [repo.changelog.node(rev[0])]:
1942 raise util.Abort(_('revision %d has unmanaged children')
1942 raise util.Abort(_('revision %d has unmanaged children')
1943 % rev[0])
1943 % rev[0])
1944 lastparent = None
1944 lastparent = None
1945
1945
1946 diffopts = self.diffopts({'git': git})
1946 diffopts = self.diffopts({'git': git})
1947 for r in rev:
1947 for r in rev:
1948 if not repo[r].mutable():
1948 if not repo[r].mutable():
1949 raise util.Abort(_('revision %d is not mutable') % r,
1949 raise util.Abort(_('revision %d is not mutable') % r,
1950 hint=_('see "hg help phases" for details'))
1950 hint=_('see "hg help phases" for details'))
1951 p1, p2 = repo.changelog.parentrevs(r)
1951 p1, p2 = repo.changelog.parentrevs(r)
1952 n = repo.changelog.node(r)
1952 n = repo.changelog.node(r)
1953 if p2 != nullrev:
1953 if p2 != nullrev:
1954 raise util.Abort(_('cannot import merge revision %d') % r)
1954 raise util.Abort(_('cannot import merge revision %d') % r)
1955 if lastparent and lastparent != r:
1955 if lastparent and lastparent != r:
1956 raise util.Abort(_('revision %d is not the parent of %d')
1956 raise util.Abort(_('revision %d is not the parent of %d')
1957 % (r, lastparent))
1957 % (r, lastparent))
1958 lastparent = p1
1958 lastparent = p1
1959
1959
1960 if not patchname:
1960 if not patchname:
1961 patchname = normname('%d.diff' % r)
1961 patchname = normname('%d.diff' % r)
1962 checkseries(patchname)
1962 checkseries(patchname)
1963 self.checkpatchname(patchname, force)
1963 self.checkpatchname(patchname, force)
1964 self.fullseries.insert(0, patchname)
1964 self.fullseries.insert(0, patchname)
1965
1965
1966 patchf = self.opener(patchname, "w")
1966 patchf = self.opener(patchname, "w")
1967 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1967 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1968 patchf.close()
1968 patchf.close()
1969
1969
1970 se = statusentry(n, patchname)
1970 se = statusentry(n, patchname)
1971 self.applied.insert(0, se)
1971 self.applied.insert(0, se)
1972
1972
1973 self.added.append(patchname)
1973 self.added.append(patchname)
1974 imported.append(patchname)
1974 imported.append(patchname)
1975 patchname = None
1975 patchname = None
1976 if rev and repo.ui.configbool('mq', 'secret', False):
1976 if rev and repo.ui.configbool('mq', 'secret', False):
1977 # if we added anything with --rev, we must move the secret root
1977 # if we added anything with --rev, we must move the secret root
1978 phases.retractboundary(repo, phases.secret, [n])
1978 phases.retractboundary(repo, phases.secret, [n])
1979 self.parseseries()
1979 self.parseseries()
1980 self.applieddirty = True
1980 self.applieddirty = True
1981 self.seriesdirty = True
1981 self.seriesdirty = True
1982
1982
1983 for i, filename in enumerate(files):
1983 for i, filename in enumerate(files):
1984 if existing:
1984 if existing:
1985 if filename == '-':
1985 if filename == '-':
1986 raise util.Abort(_('-e is incompatible with import from -'))
1986 raise util.Abort(_('-e is incompatible with import from -'))
1987 filename = normname(filename)
1987 filename = normname(filename)
1988 self.checkreservedname(filename)
1988 self.checkreservedname(filename)
1989 if util.url(filename).islocal():
1989 if util.url(filename).islocal():
1990 originpath = self.join(filename)
1990 originpath = self.join(filename)
1991 if not os.path.isfile(originpath):
1991 if not os.path.isfile(originpath):
1992 raise util.Abort(
1992 raise util.Abort(
1993 _("patch %s does not exist") % filename)
1993 _("patch %s does not exist") % filename)
1994
1994
1995 if patchname:
1995 if patchname:
1996 self.checkpatchname(patchname, force)
1996 self.checkpatchname(patchname, force)
1997
1997
1998 self.ui.write(_('renaming %s to %s\n')
1998 self.ui.write(_('renaming %s to %s\n')
1999 % (filename, patchname))
1999 % (filename, patchname))
2000 util.rename(originpath, self.join(patchname))
2000 util.rename(originpath, self.join(patchname))
2001 else:
2001 else:
2002 patchname = filename
2002 patchname = filename
2003
2003
2004 else:
2004 else:
2005 if filename == '-' and not patchname:
2005 if filename == '-' and not patchname:
2006 raise util.Abort(_('need --name to import a patch from -'))
2006 raise util.Abort(_('need --name to import a patch from -'))
2007 elif not patchname:
2007 elif not patchname:
2008 patchname = normname(os.path.basename(filename.rstrip('/')))
2008 patchname = normname(os.path.basename(filename.rstrip('/')))
2009 self.checkpatchname(patchname, force)
2009 self.checkpatchname(patchname, force)
2010 try:
2010 try:
2011 if filename == '-':
2011 if filename == '-':
2012 text = self.ui.fin.read()
2012 text = self.ui.fin.read()
2013 else:
2013 else:
2014 fp = hg.openpath(self.ui, filename)
2014 fp = hg.openpath(self.ui, filename)
2015 text = fp.read()
2015 text = fp.read()
2016 fp.close()
2016 fp.close()
2017 except (OSError, IOError):
2017 except (OSError, IOError):
2018 raise util.Abort(_("unable to read file %s") % filename)
2018 raise util.Abort(_("unable to read file %s") % filename)
2019 patchf = self.opener(patchname, "w")
2019 patchf = self.opener(patchname, "w")
2020 patchf.write(text)
2020 patchf.write(text)
2021 patchf.close()
2021 patchf.close()
2022 if not force:
2022 if not force:
2023 checkseries(patchname)
2023 checkseries(patchname)
2024 if patchname not in self.series:
2024 if patchname not in self.series:
2025 index = self.fullseriesend() + i
2025 index = self.fullseriesend() + i
2026 self.fullseries[index:index] = [patchname]
2026 self.fullseries[index:index] = [patchname]
2027 self.parseseries()
2027 self.parseseries()
2028 self.seriesdirty = True
2028 self.seriesdirty = True
2029 self.ui.warn(_("adding %s to series file\n") % patchname)
2029 self.ui.warn(_("adding %s to series file\n") % patchname)
2030 self.added.append(patchname)
2030 self.added.append(patchname)
2031 imported.append(patchname)
2031 imported.append(patchname)
2032 patchname = None
2032 patchname = None
2033
2033
2034 self.removeundo(repo)
2034 self.removeundo(repo)
2035 return imported
2035 return imported
2036
2036
2037 def fixkeepchangesopts(ui, opts):
2037 def fixkeepchangesopts(ui, opts):
2038 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2038 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2039 or opts.get('exact')):
2039 or opts.get('exact')):
2040 return opts
2040 return opts
2041 opts = dict(opts)
2041 opts = dict(opts)
2042 opts['keep_changes'] = True
2042 opts['keep_changes'] = True
2043 return opts
2043 return opts
2044
2044
2045 @command("qdelete|qremove|qrm",
2045 @command("qdelete|qremove|qrm",
2046 [('k', 'keep', None, _('keep patch file')),
2046 [('k', 'keep', None, _('keep patch file')),
2047 ('r', 'rev', [],
2047 ('r', 'rev', [],
2048 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2048 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2049 _('hg qdelete [-k] [PATCH]...'))
2049 _('hg qdelete [-k] [PATCH]...'))
2050 def delete(ui, repo, *patches, **opts):
2050 def delete(ui, repo, *patches, **opts):
2051 """remove patches from queue
2051 """remove patches from queue
2052
2052
2053 The patches must not be applied, and at least one patch is required. Exact
2053 The patches must not be applied, and at least one patch is required. Exact
2054 patch identifiers must be given. With -k/--keep, the patch files are
2054 patch identifiers must be given. With -k/--keep, the patch files are
2055 preserved in the patch directory.
2055 preserved in the patch directory.
2056
2056
2057 To stop managing a patch and move it into permanent history,
2057 To stop managing a patch and move it into permanent history,
2058 use the :hg:`qfinish` command."""
2058 use the :hg:`qfinish` command."""
2059 q = repo.mq
2059 q = repo.mq
2060 q.delete(repo, patches, opts)
2060 q.delete(repo, patches, opts)
2061 q.savedirty()
2061 q.savedirty()
2062 return 0
2062 return 0
2063
2063
2064 @command("qapplied",
2064 @command("qapplied",
2065 [('1', 'last', None, _('show only the preceding applied patch'))
2065 [('1', 'last', None, _('show only the preceding applied patch'))
2066 ] + seriesopts,
2066 ] + seriesopts,
2067 _('hg qapplied [-1] [-s] [PATCH]'))
2067 _('hg qapplied [-1] [-s] [PATCH]'))
2068 def applied(ui, repo, patch=None, **opts):
2068 def applied(ui, repo, patch=None, **opts):
2069 """print the patches already applied
2069 """print the patches already applied
2070
2070
2071 Returns 0 on success."""
2071 Returns 0 on success."""
2072
2072
2073 q = repo.mq
2073 q = repo.mq
2074
2074
2075 if patch:
2075 if patch:
2076 if patch not in q.series:
2076 if patch not in q.series:
2077 raise util.Abort(_("patch %s is not in series file") % patch)
2077 raise util.Abort(_("patch %s is not in series file") % patch)
2078 end = q.series.index(patch) + 1
2078 end = q.series.index(patch) + 1
2079 else:
2079 else:
2080 end = q.seriesend(True)
2080 end = q.seriesend(True)
2081
2081
2082 if opts.get('last') and not end:
2082 if opts.get('last') and not end:
2083 ui.write(_("no patches applied\n"))
2083 ui.write(_("no patches applied\n"))
2084 return 1
2084 return 1
2085 elif opts.get('last') and end == 1:
2085 elif opts.get('last') and end == 1:
2086 ui.write(_("only one patch applied\n"))
2086 ui.write(_("only one patch applied\n"))
2087 return 1
2087 return 1
2088 elif opts.get('last'):
2088 elif opts.get('last'):
2089 start = end - 2
2089 start = end - 2
2090 end = 1
2090 end = 1
2091 else:
2091 else:
2092 start = 0
2092 start = 0
2093
2093
2094 q.qseries(repo, length=end, start=start, status='A',
2094 q.qseries(repo, length=end, start=start, status='A',
2095 summary=opts.get('summary'))
2095 summary=opts.get('summary'))
2096
2096
2097
2097
2098 @command("qunapplied",
2098 @command("qunapplied",
2099 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2099 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2100 _('hg qunapplied [-1] [-s] [PATCH]'))
2100 _('hg qunapplied [-1] [-s] [PATCH]'))
2101 def unapplied(ui, repo, patch=None, **opts):
2101 def unapplied(ui, repo, patch=None, **opts):
2102 """print the patches not yet applied
2102 """print the patches not yet applied
2103
2103
2104 Returns 0 on success."""
2104 Returns 0 on success."""
2105
2105
2106 q = repo.mq
2106 q = repo.mq
2107 if patch:
2107 if patch:
2108 if patch not in q.series:
2108 if patch not in q.series:
2109 raise util.Abort(_("patch %s is not in series file") % patch)
2109 raise util.Abort(_("patch %s is not in series file") % patch)
2110 start = q.series.index(patch) + 1
2110 start = q.series.index(patch) + 1
2111 else:
2111 else:
2112 start = q.seriesend(True)
2112 start = q.seriesend(True)
2113
2113
2114 if start == len(q.series) and opts.get('first'):
2114 if start == len(q.series) and opts.get('first'):
2115 ui.write(_("all patches applied\n"))
2115 ui.write(_("all patches applied\n"))
2116 return 1
2116 return 1
2117
2117
2118 length = opts.get('first') and 1 or None
2118 length = opts.get('first') and 1 or None
2119 q.qseries(repo, start=start, length=length, status='U',
2119 q.qseries(repo, start=start, length=length, status='U',
2120 summary=opts.get('summary'))
2120 summary=opts.get('summary'))
2121
2121
2122 @command("qimport",
2122 @command("qimport",
2123 [('e', 'existing', None, _('import file in patch directory')),
2123 [('e', 'existing', None, _('import file in patch directory')),
2124 ('n', 'name', '',
2124 ('n', 'name', '',
2125 _('name of patch file'), _('NAME')),
2125 _('name of patch file'), _('NAME')),
2126 ('f', 'force', None, _('overwrite existing files')),
2126 ('f', 'force', None, _('overwrite existing files')),
2127 ('r', 'rev', [],
2127 ('r', 'rev', [],
2128 _('place existing revisions under mq control'), _('REV')),
2128 _('place existing revisions under mq control'), _('REV')),
2129 ('g', 'git', None, _('use git extended diff format')),
2129 ('g', 'git', None, _('use git extended diff format')),
2130 ('P', 'push', None, _('qpush after importing'))],
2130 ('P', 'push', None, _('qpush after importing'))],
2131 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2131 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2132 def qimport(ui, repo, *filename, **opts):
2132 def qimport(ui, repo, *filename, **opts):
2133 """import a patch or existing changeset
2133 """import a patch or existing changeset
2134
2134
2135 The patch is inserted into the series after the last applied
2135 The patch is inserted into the series after the last applied
2136 patch. If no patches have been applied, qimport prepends the patch
2136 patch. If no patches have been applied, qimport prepends the patch
2137 to the series.
2137 to the series.
2138
2138
2139 The patch will have the same name as its source file unless you
2139 The patch will have the same name as its source file unless you
2140 give it a new one with -n/--name.
2140 give it a new one with -n/--name.
2141
2141
2142 You can register an existing patch inside the patch directory with
2142 You can register an existing patch inside the patch directory with
2143 the -e/--existing flag.
2143 the -e/--existing flag.
2144
2144
2145 With -f/--force, an existing patch of the same name will be
2145 With -f/--force, an existing patch of the same name will be
2146 overwritten.
2146 overwritten.
2147
2147
2148 An existing changeset may be placed under mq control with -r/--rev
2148 An existing changeset may be placed under mq control with -r/--rev
2149 (e.g. qimport --rev . -n patch will place the current revision
2149 (e.g. qimport --rev . -n patch will place the current revision
2150 under mq control). With -g/--git, patches imported with --rev will
2150 under mq control). With -g/--git, patches imported with --rev will
2151 use the git diff format. See the diffs help topic for information
2151 use the git diff format. See the diffs help topic for information
2152 on why this is important for preserving rename/copy information
2152 on why this is important for preserving rename/copy information
2153 and permission changes. Use :hg:`qfinish` to remove changesets
2153 and permission changes. Use :hg:`qfinish` to remove changesets
2154 from mq control.
2154 from mq control.
2155
2155
2156 To import a patch from standard input, pass - as the patch file.
2156 To import a patch from standard input, pass - as the patch file.
2157 When importing from standard input, a patch name must be specified
2157 When importing from standard input, a patch name must be specified
2158 using the --name flag.
2158 using the --name flag.
2159
2159
2160 To import an existing patch while renaming it::
2160 To import an existing patch while renaming it::
2161
2161
2162 hg qimport -e existing-patch -n new-name
2162 hg qimport -e existing-patch -n new-name
2163
2163
2164 Returns 0 if import succeeded.
2164 Returns 0 if import succeeded.
2165 """
2165 """
2166 lock = repo.lock() # cause this may move phase
2166 lock = repo.lock() # cause this may move phase
2167 try:
2167 try:
2168 q = repo.mq
2168 q = repo.mq
2169 try:
2169 try:
2170 imported = q.qimport(
2170 imported = q.qimport(
2171 repo, filename, patchname=opts.get('name'),
2171 repo, filename, patchname=opts.get('name'),
2172 existing=opts.get('existing'), force=opts.get('force'),
2172 existing=opts.get('existing'), force=opts.get('force'),
2173 rev=opts.get('rev'), git=opts.get('git'))
2173 rev=opts.get('rev'), git=opts.get('git'))
2174 finally:
2174 finally:
2175 q.savedirty()
2175 q.savedirty()
2176 finally:
2176 finally:
2177 lock.release()
2177 lock.release()
2178
2178
2179 if imported and opts.get('push') and not opts.get('rev'):
2179 if imported and opts.get('push') and not opts.get('rev'):
2180 return q.push(repo, imported[-1])
2180 return q.push(repo, imported[-1])
2181 return 0
2181 return 0
2182
2182
2183 def qinit(ui, repo, create):
2183 def qinit(ui, repo, create):
2184 """initialize a new queue repository
2184 """initialize a new queue repository
2185
2185
2186 This command also creates a series file for ordering patches, and
2186 This command also creates a series file for ordering patches, and
2187 an mq-specific .hgignore file in the queue repository, to exclude
2187 an mq-specific .hgignore file in the queue repository, to exclude
2188 the status and guards files (these contain mostly transient state).
2188 the status and guards files (these contain mostly transient state).
2189
2189
2190 Returns 0 if initialization succeeded."""
2190 Returns 0 if initialization succeeded."""
2191 q = repo.mq
2191 q = repo.mq
2192 r = q.init(repo, create)
2192 r = q.init(repo, create)
2193 q.savedirty()
2193 q.savedirty()
2194 if r:
2194 if r:
2195 if not os.path.exists(r.wjoin('.hgignore')):
2195 if not os.path.exists(r.wjoin('.hgignore')):
2196 fp = r.wopener('.hgignore', 'w')
2196 fp = r.wopener('.hgignore', 'w')
2197 fp.write('^\\.hg\n')
2197 fp.write('^\\.hg\n')
2198 fp.write('^\\.mq\n')
2198 fp.write('^\\.mq\n')
2199 fp.write('syntax: glob\n')
2199 fp.write('syntax: glob\n')
2200 fp.write('status\n')
2200 fp.write('status\n')
2201 fp.write('guards\n')
2201 fp.write('guards\n')
2202 fp.close()
2202 fp.close()
2203 if not os.path.exists(r.wjoin('series')):
2203 if not os.path.exists(r.wjoin('series')):
2204 r.wopener('series', 'w').close()
2204 r.wopener('series', 'w').close()
2205 r[None].add(['.hgignore', 'series'])
2205 r[None].add(['.hgignore', 'series'])
2206 commands.add(ui, r)
2206 commands.add(ui, r)
2207 return 0
2207 return 0
2208
2208
2209 @command("^qinit",
2209 @command("^qinit",
2210 [('c', 'create-repo', None, _('create queue repository'))],
2210 [('c', 'create-repo', None, _('create queue repository'))],
2211 _('hg qinit [-c]'))
2211 _('hg qinit [-c]'))
2212 def init(ui, repo, **opts):
2212 def init(ui, repo, **opts):
2213 """init a new queue repository (DEPRECATED)
2213 """init a new queue repository (DEPRECATED)
2214
2214
2215 The queue repository is unversioned by default. If
2215 The queue repository is unversioned by default. If
2216 -c/--create-repo is specified, qinit will create a separate nested
2216 -c/--create-repo is specified, qinit will create a separate nested
2217 repository for patches (qinit -c may also be run later to convert
2217 repository for patches (qinit -c may also be run later to convert
2218 an unversioned patch repository into a versioned one). You can use
2218 an unversioned patch repository into a versioned one). You can use
2219 qcommit to commit changes to this queue repository.
2219 qcommit to commit changes to this queue repository.
2220
2220
2221 This command is deprecated. Without -c, it's implied by other relevant
2221 This command is deprecated. Without -c, it's implied by other relevant
2222 commands. With -c, use :hg:`init --mq` instead."""
2222 commands. With -c, use :hg:`init --mq` instead."""
2223 return qinit(ui, repo, create=opts.get('create_repo'))
2223 return qinit(ui, repo, create=opts.get('create_repo'))
2224
2224
2225 @command("qclone",
2225 @command("qclone",
2226 [('', 'pull', None, _('use pull protocol to copy metadata')),
2226 [('', 'pull', None, _('use pull protocol to copy metadata')),
2227 ('U', 'noupdate', None,
2227 ('U', 'noupdate', None,
2228 _('do not update the new working directories')),
2228 _('do not update the new working directories')),
2229 ('', 'uncompressed', None,
2229 ('', 'uncompressed', None,
2230 _('use uncompressed transfer (fast over LAN)')),
2230 _('use uncompressed transfer (fast over LAN)')),
2231 ('p', 'patches', '',
2231 ('p', 'patches', '',
2232 _('location of source patch repository'), _('REPO')),
2232 _('location of source patch repository'), _('REPO')),
2233 ] + commands.remoteopts,
2233 ] + commands.remoteopts,
2234 _('hg qclone [OPTION]... SOURCE [DEST]'))
2234 _('hg qclone [OPTION]... SOURCE [DEST]'))
2235 def clone(ui, source, dest=None, **opts):
2235 def clone(ui, source, dest=None, **opts):
2236 '''clone main and patch repository at same time
2236 '''clone main and patch repository at same time
2237
2237
2238 If source is local, destination will have no patches applied. If
2238 If source is local, destination will have no patches applied. If
2239 source is remote, this command can not check if patches are
2239 source is remote, this command can not check if patches are
2240 applied in source, so cannot guarantee that patches are not
2240 applied in source, so cannot guarantee that patches are not
2241 applied in destination. If you clone remote repository, be sure
2241 applied in destination. If you clone remote repository, be sure
2242 before that it has no patches applied.
2242 before that it has no patches applied.
2243
2243
2244 Source patch repository is looked for in <src>/.hg/patches by
2244 Source patch repository is looked for in <src>/.hg/patches by
2245 default. Use -p <url> to change.
2245 default. Use -p <url> to change.
2246
2246
2247 The patch directory must be a nested Mercurial repository, as
2247 The patch directory must be a nested Mercurial repository, as
2248 would be created by :hg:`init --mq`.
2248 would be created by :hg:`init --mq`.
2249
2249
2250 Return 0 on success.
2250 Return 0 on success.
2251 '''
2251 '''
2252 def patchdir(repo):
2252 def patchdir(repo):
2253 """compute a patch repo url from a repo object"""
2253 """compute a patch repo url from a repo object"""
2254 url = repo.url()
2254 url = repo.url()
2255 if url.endswith('/'):
2255 if url.endswith('/'):
2256 url = url[:-1]
2256 url = url[:-1]
2257 return url + '/.hg/patches'
2257 return url + '/.hg/patches'
2258
2258
2259 # main repo (destination and sources)
2259 # main repo (destination and sources)
2260 if dest is None:
2260 if dest is None:
2261 dest = hg.defaultdest(source)
2261 dest = hg.defaultdest(source)
2262 sr = hg.peer(ui, opts, ui.expandpath(source))
2262 sr = hg.peer(ui, opts, ui.expandpath(source))
2263
2263
2264 # patches repo (source only)
2264 # patches repo (source only)
2265 if opts.get('patches'):
2265 if opts.get('patches'):
2266 patchespath = ui.expandpath(opts.get('patches'))
2266 patchespath = ui.expandpath(opts.get('patches'))
2267 else:
2267 else:
2268 patchespath = patchdir(sr)
2268 patchespath = patchdir(sr)
2269 try:
2269 try:
2270 hg.peer(ui, opts, patchespath)
2270 hg.peer(ui, opts, patchespath)
2271 except error.RepoError:
2271 except error.RepoError:
2272 raise util.Abort(_('versioned patch repository not found'
2272 raise util.Abort(_('versioned patch repository not found'
2273 ' (see init --mq)'))
2273 ' (see init --mq)'))
2274 qbase, destrev = None, None
2274 qbase, destrev = None, None
2275 if sr.local():
2275 if sr.local():
2276 repo = sr.local()
2276 repo = sr.local()
2277 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2277 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2278 qbase = repo.mq.applied[0].node
2278 qbase = repo.mq.applied[0].node
2279 if not hg.islocal(dest):
2279 if not hg.islocal(dest):
2280 heads = set(repo.heads())
2280 heads = set(repo.heads())
2281 destrev = list(heads.difference(repo.heads(qbase)))
2281 destrev = list(heads.difference(repo.heads(qbase)))
2282 destrev.append(repo.changelog.parents(qbase)[0])
2282 destrev.append(repo.changelog.parents(qbase)[0])
2283 elif sr.capable('lookup'):
2283 elif sr.capable('lookup'):
2284 try:
2284 try:
2285 qbase = sr.lookup('qbase')
2285 qbase = sr.lookup('qbase')
2286 except error.RepoError:
2286 except error.RepoError:
2287 pass
2287 pass
2288
2288
2289 ui.note(_('cloning main repository\n'))
2289 ui.note(_('cloning main repository\n'))
2290 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2290 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2291 pull=opts.get('pull'),
2291 pull=opts.get('pull'),
2292 rev=destrev,
2292 rev=destrev,
2293 update=False,
2293 update=False,
2294 stream=opts.get('uncompressed'))
2294 stream=opts.get('uncompressed'))
2295
2295
2296 ui.note(_('cloning patch repository\n'))
2296 ui.note(_('cloning patch repository\n'))
2297 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2297 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2298 pull=opts.get('pull'), update=not opts.get('noupdate'),
2298 pull=opts.get('pull'), update=not opts.get('noupdate'),
2299 stream=opts.get('uncompressed'))
2299 stream=opts.get('uncompressed'))
2300
2300
2301 if dr.local():
2301 if dr.local():
2302 repo = dr.local()
2302 repo = dr.local()
2303 if qbase:
2303 if qbase:
2304 ui.note(_('stripping applied patches from destination '
2304 ui.note(_('stripping applied patches from destination '
2305 'repository\n'))
2305 'repository\n'))
2306 strip(ui, repo, [qbase], update=False, backup=None)
2306 strip(ui, repo, [qbase], update=False, backup=None)
2307 if not opts.get('noupdate'):
2307 if not opts.get('noupdate'):
2308 ui.note(_('updating destination repository\n'))
2308 ui.note(_('updating destination repository\n'))
2309 hg.update(repo, repo.changelog.tip())
2309 hg.update(repo, repo.changelog.tip())
2310
2310
2311 @command("qcommit|qci",
2311 @command("qcommit|qci",
2312 commands.table["^commit|ci"][1],
2312 commands.table["^commit|ci"][1],
2313 _('hg qcommit [OPTION]... [FILE]...'))
2313 _('hg qcommit [OPTION]... [FILE]...'))
2314 def commit(ui, repo, *pats, **opts):
2314 def commit(ui, repo, *pats, **opts):
2315 """commit changes in the queue repository (DEPRECATED)
2315 """commit changes in the queue repository (DEPRECATED)
2316
2316
2317 This command is deprecated; use :hg:`commit --mq` instead."""
2317 This command is deprecated; use :hg:`commit --mq` instead."""
2318 q = repo.mq
2318 q = repo.mq
2319 r = q.qrepo()
2319 r = q.qrepo()
2320 if not r:
2320 if not r:
2321 raise util.Abort('no queue repository')
2321 raise util.Abort('no queue repository')
2322 commands.commit(r.ui, r, *pats, **opts)
2322 commands.commit(r.ui, r, *pats, **opts)
2323
2323
2324 @command("qseries",
2324 @command("qseries",
2325 [('m', 'missing', None, _('print patches not in series')),
2325 [('m', 'missing', None, _('print patches not in series')),
2326 ] + seriesopts,
2326 ] + seriesopts,
2327 _('hg qseries [-ms]'))
2327 _('hg qseries [-ms]'))
2328 def series(ui, repo, **opts):
2328 def series(ui, repo, **opts):
2329 """print the entire series file
2329 """print the entire series file
2330
2330
2331 Returns 0 on success."""
2331 Returns 0 on success."""
2332 repo.mq.qseries(repo, missing=opts.get('missing'),
2332 repo.mq.qseries(repo, missing=opts.get('missing'),
2333 summary=opts.get('summary'))
2333 summary=opts.get('summary'))
2334 return 0
2334 return 0
2335
2335
2336 @command("qtop", seriesopts, _('hg qtop [-s]'))
2336 @command("qtop", seriesopts, _('hg qtop [-s]'))
2337 def top(ui, repo, **opts):
2337 def top(ui, repo, **opts):
2338 """print the name of the current patch
2338 """print the name of the current patch
2339
2339
2340 Returns 0 on success."""
2340 Returns 0 on success."""
2341 q = repo.mq
2341 q = repo.mq
2342 t = q.applied and q.seriesend(True) or 0
2342 t = q.applied and q.seriesend(True) or 0
2343 if t:
2343 if t:
2344 q.qseries(repo, start=t - 1, length=1, status='A',
2344 q.qseries(repo, start=t - 1, length=1, status='A',
2345 summary=opts.get('summary'))
2345 summary=opts.get('summary'))
2346 else:
2346 else:
2347 ui.write(_("no patches applied\n"))
2347 ui.write(_("no patches applied\n"))
2348 return 1
2348 return 1
2349
2349
2350 @command("qnext", seriesopts, _('hg qnext [-s]'))
2350 @command("qnext", seriesopts, _('hg qnext [-s]'))
2351 def next(ui, repo, **opts):
2351 def next(ui, repo, **opts):
2352 """print the name of the next pushable patch
2352 """print the name of the next pushable patch
2353
2353
2354 Returns 0 on success."""
2354 Returns 0 on success."""
2355 q = repo.mq
2355 q = repo.mq
2356 end = q.seriesend()
2356 end = q.seriesend()
2357 if end == len(q.series):
2357 if end == len(q.series):
2358 ui.write(_("all patches applied\n"))
2358 ui.write(_("all patches applied\n"))
2359 return 1
2359 return 1
2360 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2360 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2361
2361
2362 @command("qprev", seriesopts, _('hg qprev [-s]'))
2362 @command("qprev", seriesopts, _('hg qprev [-s]'))
2363 def prev(ui, repo, **opts):
2363 def prev(ui, repo, **opts):
2364 """print the name of the preceding applied patch
2364 """print the name of the preceding applied patch
2365
2365
2366 Returns 0 on success."""
2366 Returns 0 on success."""
2367 q = repo.mq
2367 q = repo.mq
2368 l = len(q.applied)
2368 l = len(q.applied)
2369 if l == 1:
2369 if l == 1:
2370 ui.write(_("only one patch applied\n"))
2370 ui.write(_("only one patch applied\n"))
2371 return 1
2371 return 1
2372 if not l:
2372 if not l:
2373 ui.write(_("no patches applied\n"))
2373 ui.write(_("no patches applied\n"))
2374 return 1
2374 return 1
2375 idx = q.series.index(q.applied[-2].name)
2375 idx = q.series.index(q.applied[-2].name)
2376 q.qseries(repo, start=idx, length=1, status='A',
2376 q.qseries(repo, start=idx, length=1, status='A',
2377 summary=opts.get('summary'))
2377 summary=opts.get('summary'))
2378
2378
2379 def setupheaderopts(ui, opts):
2379 def setupheaderopts(ui, opts):
2380 if not opts.get('user') and opts.get('currentuser'):
2380 if not opts.get('user') and opts.get('currentuser'):
2381 opts['user'] = ui.username()
2381 opts['user'] = ui.username()
2382 if not opts.get('date') and opts.get('currentdate'):
2382 if not opts.get('date') and opts.get('currentdate'):
2383 opts['date'] = "%d %d" % util.makedate()
2383 opts['date'] = "%d %d" % util.makedate()
2384
2384
2385 @command("^qnew",
2385 @command("^qnew",
2386 [('e', 'edit', None, _('edit commit message')),
2386 [('e', 'edit', None, _('edit commit message')),
2387 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2387 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2388 ('g', 'git', None, _('use git extended diff format')),
2388 ('g', 'git', None, _('use git extended diff format')),
2389 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2389 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2390 ('u', 'user', '',
2390 ('u', 'user', '',
2391 _('add "From: <USER>" to patch'), _('USER')),
2391 _('add "From: <USER>" to patch'), _('USER')),
2392 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2392 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2393 ('d', 'date', '',
2393 ('d', 'date', '',
2394 _('add "Date: <DATE>" to patch'), _('DATE'))
2394 _('add "Date: <DATE>" to patch'), _('DATE'))
2395 ] + commands.walkopts + commands.commitopts,
2395 ] + commands.walkopts + commands.commitopts,
2396 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'))
2396 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'))
2397 def new(ui, repo, patch, *args, **opts):
2397 def new(ui, repo, patch, *args, **opts):
2398 """create a new patch
2398 """create a new patch
2399
2399
2400 qnew creates a new patch on top of the currently-applied patch (if
2400 qnew creates a new patch on top of the currently-applied patch (if
2401 any). The patch will be initialized with any outstanding changes
2401 any). The patch will be initialized with any outstanding changes
2402 in the working directory. You may also use -I/--include,
2402 in the working directory. You may also use -I/--include,
2403 -X/--exclude, and/or a list of files after the patch name to add
2403 -X/--exclude, and/or a list of files after the patch name to add
2404 only changes to matching files to the new patch, leaving the rest
2404 only changes to matching files to the new patch, leaving the rest
2405 as uncommitted modifications.
2405 as uncommitted modifications.
2406
2406
2407 -u/--user and -d/--date can be used to set the (given) user and
2407 -u/--user and -d/--date can be used to set the (given) user and
2408 date, respectively. -U/--currentuser and -D/--currentdate set user
2408 date, respectively. -U/--currentuser and -D/--currentdate set user
2409 to current user and date to current date.
2409 to current user and date to current date.
2410
2410
2411 -e/--edit, -m/--message or -l/--logfile set the patch header as
2411 -e/--edit, -m/--message or -l/--logfile set the patch header as
2412 well as the commit message. If none is specified, the header is
2412 well as the commit message. If none is specified, the header is
2413 empty and the commit message is '[mq]: PATCH'.
2413 empty and the commit message is '[mq]: PATCH'.
2414
2414
2415 Use the -g/--git option to keep the patch in the git extended diff
2415 Use the -g/--git option to keep the patch in the git extended diff
2416 format. Read the diffs help topic for more information on why this
2416 format. Read the diffs help topic for more information on why this
2417 is important for preserving permission changes and copy/rename
2417 is important for preserving permission changes and copy/rename
2418 information.
2418 information.
2419
2419
2420 Returns 0 on successful creation of a new patch.
2420 Returns 0 on successful creation of a new patch.
2421 """
2421 """
2422 msg = cmdutil.logmessage(ui, opts)
2422 msg = cmdutil.logmessage(ui, opts)
2423 def getmsg():
2423 def getmsg():
2424 return ui.edit(msg, opts.get('user') or ui.username())
2424 return ui.edit(msg, opts.get('user') or ui.username())
2425 q = repo.mq
2425 q = repo.mq
2426 opts['msg'] = msg
2426 opts['msg'] = msg
2427 if opts.get('edit'):
2427 if opts.get('edit'):
2428 opts['msg'] = getmsg
2428 opts['msg'] = getmsg
2429 else:
2429 else:
2430 opts['msg'] = msg
2430 opts['msg'] = msg
2431 setupheaderopts(ui, opts)
2431 setupheaderopts(ui, opts)
2432 q.new(repo, patch, *args, **opts)
2432 q.new(repo, patch, *args, **opts)
2433 q.savedirty()
2433 q.savedirty()
2434 return 0
2434 return 0
2435
2435
2436 @command("^qrefresh",
2436 @command("^qrefresh",
2437 [('e', 'edit', None, _('edit commit message')),
2437 [('e', 'edit', None, _('edit commit message')),
2438 ('g', 'git', None, _('use git extended diff format')),
2438 ('g', 'git', None, _('use git extended diff format')),
2439 ('s', 'short', None,
2439 ('s', 'short', None,
2440 _('refresh only files already in the patch and specified files')),
2440 _('refresh only files already in the patch and specified files')),
2441 ('U', 'currentuser', None,
2441 ('U', 'currentuser', None,
2442 _('add/update author field in patch with current user')),
2442 _('add/update author field in patch with current user')),
2443 ('u', 'user', '',
2443 ('u', 'user', '',
2444 _('add/update author field in patch with given user'), _('USER')),
2444 _('add/update author field in patch with given user'), _('USER')),
2445 ('D', 'currentdate', None,
2445 ('D', 'currentdate', None,
2446 _('add/update date field in patch with current date')),
2446 _('add/update date field in patch with current date')),
2447 ('d', 'date', '',
2447 ('d', 'date', '',
2448 _('add/update date field in patch with given date'), _('DATE'))
2448 _('add/update date field in patch with given date'), _('DATE'))
2449 ] + commands.walkopts + commands.commitopts,
2449 ] + commands.walkopts + commands.commitopts,
2450 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'))
2450 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'))
2451 def refresh(ui, repo, *pats, **opts):
2451 def refresh(ui, repo, *pats, **opts):
2452 """update the current patch
2452 """update the current patch
2453
2453
2454 If any file patterns are provided, the refreshed patch will
2454 If any file patterns are provided, the refreshed patch will
2455 contain only the modifications that match those patterns; the
2455 contain only the modifications that match those patterns; the
2456 remaining modifications will remain in the working directory.
2456 remaining modifications will remain in the working directory.
2457
2457
2458 If -s/--short is specified, files currently included in the patch
2458 If -s/--short is specified, files currently included in the patch
2459 will be refreshed just like matched files and remain in the patch.
2459 will be refreshed just like matched files and remain in the patch.
2460
2460
2461 If -e/--edit is specified, Mercurial will start your configured editor for
2461 If -e/--edit is specified, Mercurial will start your configured editor for
2462 you to enter a message. In case qrefresh fails, you will find a backup of
2462 you to enter a message. In case qrefresh fails, you will find a backup of
2463 your message in ``.hg/last-message.txt``.
2463 your message in ``.hg/last-message.txt``.
2464
2464
2465 hg add/remove/copy/rename work as usual, though you might want to
2465 hg add/remove/copy/rename work as usual, though you might want to
2466 use git-style patches (-g/--git or [diff] git=1) to track copies
2466 use git-style patches (-g/--git or [diff] git=1) to track copies
2467 and renames. See the diffs help topic for more information on the
2467 and renames. See the diffs help topic for more information on the
2468 git diff format.
2468 git diff format.
2469
2469
2470 Returns 0 on success.
2470 Returns 0 on success.
2471 """
2471 """
2472 q = repo.mq
2472 q = repo.mq
2473 message = cmdutil.logmessage(ui, opts)
2473 message = cmdutil.logmessage(ui, opts)
2474 if opts.get('edit'):
2474 if opts.get('edit'):
2475 if not q.applied:
2475 if not q.applied:
2476 ui.write(_("no patches applied\n"))
2476 ui.write(_("no patches applied\n"))
2477 return 1
2477 return 1
2478 if message:
2478 if message:
2479 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2479 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2480 patch = q.applied[-1].name
2480 patch = q.applied[-1].name
2481 ph = patchheader(q.join(patch), q.plainmode)
2481 ph = patchheader(q.join(patch), q.plainmode)
2482 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2482 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2483 # We don't want to lose the patch message if qrefresh fails (issue2062)
2483 # We don't want to lose the patch message if qrefresh fails (issue2062)
2484 repo.savecommitmessage(message)
2484 repo.savecommitmessage(message)
2485 setupheaderopts(ui, opts)
2485 setupheaderopts(ui, opts)
2486 wlock = repo.wlock()
2486 wlock = repo.wlock()
2487 try:
2487 try:
2488 ret = q.refresh(repo, pats, msg=message, **opts)
2488 ret = q.refresh(repo, pats, msg=message, **opts)
2489 q.savedirty()
2489 q.savedirty()
2490 return ret
2490 return ret
2491 finally:
2491 finally:
2492 wlock.release()
2492 wlock.release()
2493
2493
2494 @command("^qdiff",
2494 @command("^qdiff",
2495 commands.diffopts + commands.diffopts2 + commands.walkopts,
2495 commands.diffopts + commands.diffopts2 + commands.walkopts,
2496 _('hg qdiff [OPTION]... [FILE]...'))
2496 _('hg qdiff [OPTION]... [FILE]...'))
2497 def diff(ui, repo, *pats, **opts):
2497 def diff(ui, repo, *pats, **opts):
2498 """diff of the current patch and subsequent modifications
2498 """diff of the current patch and subsequent modifications
2499
2499
2500 Shows a diff which includes the current patch as well as any
2500 Shows a diff which includes the current patch as well as any
2501 changes which have been made in the working directory since the
2501 changes which have been made in the working directory since the
2502 last refresh (thus showing what the current patch would become
2502 last refresh (thus showing what the current patch would become
2503 after a qrefresh).
2503 after a qrefresh).
2504
2504
2505 Use :hg:`diff` if you only want to see the changes made since the
2505 Use :hg:`diff` if you only want to see the changes made since the
2506 last qrefresh, or :hg:`export qtip` if you want to see changes
2506 last qrefresh, or :hg:`export qtip` if you want to see changes
2507 made by the current patch without including changes made since the
2507 made by the current patch without including changes made since the
2508 qrefresh.
2508 qrefresh.
2509
2509
2510 Returns 0 on success.
2510 Returns 0 on success.
2511 """
2511 """
2512 repo.mq.diff(repo, pats, opts)
2512 repo.mq.diff(repo, pats, opts)
2513 return 0
2513 return 0
2514
2514
2515 @command('qfold',
2515 @command('qfold',
2516 [('e', 'edit', None, _('edit patch header')),
2516 [('e', 'edit', None, _('edit patch header')),
2517 ('k', 'keep', None, _('keep folded patch files')),
2517 ('k', 'keep', None, _('keep folded patch files')),
2518 ] + commands.commitopts,
2518 ] + commands.commitopts,
2519 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2519 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2520 def fold(ui, repo, *files, **opts):
2520 def fold(ui, repo, *files, **opts):
2521 """fold the named patches into the current patch
2521 """fold the named patches into the current patch
2522
2522
2523 Patches must not yet be applied. Each patch will be successively
2523 Patches must not yet be applied. Each patch will be successively
2524 applied to the current patch in the order given. If all the
2524 applied to the current patch in the order given. If all the
2525 patches apply successfully, the current patch will be refreshed
2525 patches apply successfully, the current patch will be refreshed
2526 with the new cumulative patch, and the folded patches will be
2526 with the new cumulative patch, and the folded patches will be
2527 deleted. With -k/--keep, the folded patch files will not be
2527 deleted. With -k/--keep, the folded patch files will not be
2528 removed afterwards.
2528 removed afterwards.
2529
2529
2530 The header for each folded patch will be concatenated with the
2530 The header for each folded patch will be concatenated with the
2531 current patch header, separated by a line of ``* * *``.
2531 current patch header, separated by a line of ``* * *``.
2532
2532
2533 Returns 0 on success."""
2533 Returns 0 on success."""
2534 q = repo.mq
2534 q = repo.mq
2535 if not files:
2535 if not files:
2536 raise util.Abort(_('qfold requires at least one patch name'))
2536 raise util.Abort(_('qfold requires at least one patch name'))
2537 if not q.checktoppatch(repo)[0]:
2537 if not q.checktoppatch(repo)[0]:
2538 raise util.Abort(_('no patches applied'))
2538 raise util.Abort(_('no patches applied'))
2539 q.checklocalchanges(repo)
2539 q.checklocalchanges(repo)
2540
2540
2541 message = cmdutil.logmessage(ui, opts)
2541 message = cmdutil.logmessage(ui, opts)
2542 if opts.get('edit'):
2542 if opts.get('edit'):
2543 if message:
2543 if message:
2544 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2544 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2545
2545
2546 parent = q.lookup('qtip')
2546 parent = q.lookup('qtip')
2547 patches = []
2547 patches = []
2548 messages = []
2548 messages = []
2549 for f in files:
2549 for f in files:
2550 p = q.lookup(f)
2550 p = q.lookup(f)
2551 if p in patches or p == parent:
2551 if p in patches or p == parent:
2552 ui.warn(_('skipping already folded patch %s\n') % p)
2552 ui.warn(_('skipping already folded patch %s\n') % p)
2553 if q.isapplied(p):
2553 if q.isapplied(p):
2554 raise util.Abort(_('qfold cannot fold already applied patch %s')
2554 raise util.Abort(_('qfold cannot fold already applied patch %s')
2555 % p)
2555 % p)
2556 patches.append(p)
2556 patches.append(p)
2557
2557
2558 for p in patches:
2558 for p in patches:
2559 if not message:
2559 if not message:
2560 ph = patchheader(q.join(p), q.plainmode)
2560 ph = patchheader(q.join(p), q.plainmode)
2561 if ph.message:
2561 if ph.message:
2562 messages.append(ph.message)
2562 messages.append(ph.message)
2563 pf = q.join(p)
2563 pf = q.join(p)
2564 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2564 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2565 if not patchsuccess:
2565 if not patchsuccess:
2566 raise util.Abort(_('error folding patch %s') % p)
2566 raise util.Abort(_('error folding patch %s') % p)
2567
2567
2568 if not message:
2568 if not message:
2569 ph = patchheader(q.join(parent), q.plainmode)
2569 ph = patchheader(q.join(parent), q.plainmode)
2570 message, user = ph.message, ph.user
2570 message, user = ph.message, ph.user
2571 for msg in messages:
2571 for msg in messages:
2572 if msg:
2572 if msg:
2573 if message:
2573 if message:
2574 message.append('* * *')
2574 message.append('* * *')
2575 message.extend(msg)
2575 message.extend(msg)
2576 message = '\n'.join(message)
2576 message = '\n'.join(message)
2577
2577
2578 if opts.get('edit'):
2578 if opts.get('edit'):
2579 message = ui.edit(message, user or ui.username())
2579 message = ui.edit(message, user or ui.username())
2580
2580
2581 diffopts = q.patchopts(q.diffopts(), *patches)
2581 diffopts = q.patchopts(q.diffopts(), *patches)
2582 wlock = repo.wlock()
2582 wlock = repo.wlock()
2583 try:
2583 try:
2584 q.refresh(repo, msg=message, git=diffopts.git)
2584 q.refresh(repo, msg=message, git=diffopts.git)
2585 q.delete(repo, patches, opts)
2585 q.delete(repo, patches, opts)
2586 q.savedirty()
2586 q.savedirty()
2587 finally:
2587 finally:
2588 wlock.release()
2588 wlock.release()
2589
2589
2590 @command("qgoto",
2590 @command("qgoto",
2591 [('', 'keep-changes', None,
2591 [('', 'keep-changes', None,
2592 _('tolerate non-conflicting local changes')),
2592 _('tolerate non-conflicting local changes')),
2593 ('f', 'force', None, _('overwrite any local changes')),
2593 ('f', 'force', None, _('overwrite any local changes')),
2594 ('', 'no-backup', None, _('do not save backup copies of files'))],
2594 ('', 'no-backup', None, _('do not save backup copies of files'))],
2595 _('hg qgoto [OPTION]... PATCH'))
2595 _('hg qgoto [OPTION]... PATCH'))
2596 def goto(ui, repo, patch, **opts):
2596 def goto(ui, repo, patch, **opts):
2597 '''push or pop patches until named patch is at top of stack
2597 '''push or pop patches until named patch is at top of stack
2598
2598
2599 Returns 0 on success.'''
2599 Returns 0 on success.'''
2600 opts = fixkeepchangesopts(ui, opts)
2600 opts = fixkeepchangesopts(ui, opts)
2601 q = repo.mq
2601 q = repo.mq
2602 patch = q.lookup(patch)
2602 patch = q.lookup(patch)
2603 nobackup = opts.get('no_backup')
2603 nobackup = opts.get('no_backup')
2604 keepchanges = opts.get('keep_changes')
2604 keepchanges = opts.get('keep_changes')
2605 if q.isapplied(patch):
2605 if q.isapplied(patch):
2606 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2606 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2607 keepchanges=keepchanges)
2607 keepchanges=keepchanges)
2608 else:
2608 else:
2609 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2609 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2610 keepchanges=keepchanges)
2610 keepchanges=keepchanges)
2611 q.savedirty()
2611 q.savedirty()
2612 return ret
2612 return ret
2613
2613
2614 @command("qguard",
2614 @command("qguard",
2615 [('l', 'list', None, _('list all patches and guards')),
2615 [('l', 'list', None, _('list all patches and guards')),
2616 ('n', 'none', None, _('drop all guards'))],
2616 ('n', 'none', None, _('drop all guards'))],
2617 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2617 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2618 def guard(ui, repo, *args, **opts):
2618 def guard(ui, repo, *args, **opts):
2619 '''set or print guards for a patch
2619 '''set or print guards for a patch
2620
2620
2621 Guards control whether a patch can be pushed. A patch with no
2621 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
2622 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
2623 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
2624 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2625 has activated it.
2625 has activated it.
2626
2626
2627 With no arguments, print the currently active guards.
2627 With no arguments, print the currently active guards.
2628 With arguments, set guards for the named patch.
2628 With arguments, set guards for the named patch.
2629
2629
2630 .. note::
2630 .. note::
2631
2631
2632 Specifying negative guards now requires '--'.
2632 Specifying negative guards now requires '--'.
2633
2633
2634 To set guards on another patch::
2634 To set guards on another patch::
2635
2635
2636 hg qguard other.patch -- +2.6.17 -stable
2636 hg qguard other.patch -- +2.6.17 -stable
2637
2637
2638 Returns 0 on success.
2638 Returns 0 on success.
2639 '''
2639 '''
2640 def status(idx):
2640 def status(idx):
2641 guards = q.seriesguards[idx] or ['unguarded']
2641 guards = q.seriesguards[idx] or ['unguarded']
2642 if q.series[idx] in applied:
2642 if q.series[idx] in applied:
2643 state = 'applied'
2643 state = 'applied'
2644 elif q.pushable(idx)[0]:
2644 elif q.pushable(idx)[0]:
2645 state = 'unapplied'
2645 state = 'unapplied'
2646 else:
2646 else:
2647 state = 'guarded'
2647 state = 'guarded'
2648 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2648 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2649 ui.write('%s: ' % ui.label(q.series[idx], label))
2649 ui.write('%s: ' % ui.label(q.series[idx], label))
2650
2650
2651 for i, guard in enumerate(guards):
2651 for i, guard in enumerate(guards):
2652 if guard.startswith('+'):
2652 if guard.startswith('+'):
2653 ui.write(guard, label='qguard.positive')
2653 ui.write(guard, label='qguard.positive')
2654 elif guard.startswith('-'):
2654 elif guard.startswith('-'):
2655 ui.write(guard, label='qguard.negative')
2655 ui.write(guard, label='qguard.negative')
2656 else:
2656 else:
2657 ui.write(guard, label='qguard.unguarded')
2657 ui.write(guard, label='qguard.unguarded')
2658 if i != len(guards) - 1:
2658 if i != len(guards) - 1:
2659 ui.write(' ')
2659 ui.write(' ')
2660 ui.write('\n')
2660 ui.write('\n')
2661 q = repo.mq
2661 q = repo.mq
2662 applied = set(p.name for p in q.applied)
2662 applied = set(p.name for p in q.applied)
2663 patch = None
2663 patch = None
2664 args = list(args)
2664 args = list(args)
2665 if opts.get('list'):
2665 if opts.get('list'):
2666 if args or opts.get('none'):
2666 if args or opts.get('none'):
2667 raise util.Abort(_('cannot mix -l/--list with options or '
2667 raise util.Abort(_('cannot mix -l/--list with options or '
2668 'arguments'))
2668 'arguments'))
2669 for i in xrange(len(q.series)):
2669 for i in xrange(len(q.series)):
2670 status(i)
2670 status(i)
2671 return
2671 return
2672 if not args or args[0][0:1] in '-+':
2672 if not args or args[0][0:1] in '-+':
2673 if not q.applied:
2673 if not q.applied:
2674 raise util.Abort(_('no patches applied'))
2674 raise util.Abort(_('no patches applied'))
2675 patch = q.applied[-1].name
2675 patch = q.applied[-1].name
2676 if patch is None and args[0][0:1] not in '-+':
2676 if patch is None and args[0][0:1] not in '-+':
2677 patch = args.pop(0)
2677 patch = args.pop(0)
2678 if patch is None:
2678 if patch is None:
2679 raise util.Abort(_('no patch to work with'))
2679 raise util.Abort(_('no patch to work with'))
2680 if args or opts.get('none'):
2680 if args or opts.get('none'):
2681 idx = q.findseries(patch)
2681 idx = q.findseries(patch)
2682 if idx is None:
2682 if idx is None:
2683 raise util.Abort(_('no patch named %s') % patch)
2683 raise util.Abort(_('no patch named %s') % patch)
2684 q.setguards(idx, args)
2684 q.setguards(idx, args)
2685 q.savedirty()
2685 q.savedirty()
2686 else:
2686 else:
2687 status(q.series.index(q.lookup(patch)))
2687 status(q.series.index(q.lookup(patch)))
2688
2688
2689 @command("qheader", [], _('hg qheader [PATCH]'))
2689 @command("qheader", [], _('hg qheader [PATCH]'))
2690 def header(ui, repo, patch=None):
2690 def header(ui, repo, patch=None):
2691 """print the header of the topmost or specified patch
2691 """print the header of the topmost or specified patch
2692
2692
2693 Returns 0 on success."""
2693 Returns 0 on success."""
2694 q = repo.mq
2694 q = repo.mq
2695
2695
2696 if patch:
2696 if patch:
2697 patch = q.lookup(patch)
2697 patch = q.lookup(patch)
2698 else:
2698 else:
2699 if not q.applied:
2699 if not q.applied:
2700 ui.write(_('no patches applied\n'))
2700 ui.write(_('no patches applied\n'))
2701 return 1
2701 return 1
2702 patch = q.lookup('qtip')
2702 patch = q.lookup('qtip')
2703 ph = patchheader(q.join(patch), q.plainmode)
2703 ph = patchheader(q.join(patch), q.plainmode)
2704
2704
2705 ui.write('\n'.join(ph.message) + '\n')
2705 ui.write('\n'.join(ph.message) + '\n')
2706
2706
2707 def lastsavename(path):
2707 def lastsavename(path):
2708 (directory, base) = os.path.split(path)
2708 (directory, base) = os.path.split(path)
2709 names = os.listdir(directory)
2709 names = os.listdir(directory)
2710 namere = re.compile("%s.([0-9]+)" % base)
2710 namere = re.compile("%s.([0-9]+)" % base)
2711 maxindex = None
2711 maxindex = None
2712 maxname = None
2712 maxname = None
2713 for f in names:
2713 for f in names:
2714 m = namere.match(f)
2714 m = namere.match(f)
2715 if m:
2715 if m:
2716 index = int(m.group(1))
2716 index = int(m.group(1))
2717 if maxindex is None or index > maxindex:
2717 if maxindex is None or index > maxindex:
2718 maxindex = index
2718 maxindex = index
2719 maxname = f
2719 maxname = f
2720 if maxname:
2720 if maxname:
2721 return (os.path.join(directory, maxname), maxindex)
2721 return (os.path.join(directory, maxname), maxindex)
2722 return (None, None)
2722 return (None, None)
2723
2723
2724 def savename(path):
2724 def savename(path):
2725 (last, index) = lastsavename(path)
2725 (last, index) = lastsavename(path)
2726 if last is None:
2726 if last is None:
2727 index = 0
2727 index = 0
2728 newpath = path + ".%d" % (index + 1)
2728 newpath = path + ".%d" % (index + 1)
2729 return newpath
2729 return newpath
2730
2730
2731 @command("^qpush",
2731 @command("^qpush",
2732 [('', 'keep-changes', None,
2732 [('', 'keep-changes', None,
2733 _('tolerate non-conflicting local changes')),
2733 _('tolerate non-conflicting local changes')),
2734 ('f', 'force', None, _('apply on top of local changes')),
2734 ('f', 'force', None, _('apply on top of local changes')),
2735 ('e', 'exact', None,
2735 ('e', 'exact', None,
2736 _('apply the target patch to its recorded parent')),
2736 _('apply the target patch to its recorded parent')),
2737 ('l', 'list', None, _('list patch name in commit text')),
2737 ('l', 'list', None, _('list patch name in commit text')),
2738 ('a', 'all', None, _('apply all patches')),
2738 ('a', 'all', None, _('apply all patches')),
2739 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2739 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2740 ('n', 'name', '',
2740 ('n', 'name', '',
2741 _('merge queue name (DEPRECATED)'), _('NAME')),
2741 _('merge queue name (DEPRECATED)'), _('NAME')),
2742 ('', 'move', None,
2742 ('', 'move', None,
2743 _('reorder patch series and apply only the patch')),
2743 _('reorder patch series and apply only the patch')),
2744 ('', 'no-backup', None, _('do not save backup copies of files'))],
2744 ('', 'no-backup', None, _('do not save backup copies of files'))],
2745 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2745 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2746 def push(ui, repo, patch=None, **opts):
2746 def push(ui, repo, patch=None, **opts):
2747 """push the next patch onto the stack
2747 """push the next patch onto the stack
2748
2748
2749 By default, abort if the working directory contains uncommitted
2749 By default, abort if the working directory contains uncommitted
2750 changes. With --keep-changes, abort only if the uncommitted files
2750 changes. With --keep-changes, abort only if the uncommitted files
2751 overlap with patched files. With -f/--force, backup and patch over
2751 overlap with patched files. With -f/--force, backup and patch over
2752 uncommitted changes.
2752 uncommitted changes.
2753
2753
2754 Return 0 on success.
2754 Return 0 on success.
2755 """
2755 """
2756 q = repo.mq
2756 q = repo.mq
2757 mergeq = None
2757 mergeq = None
2758
2758
2759 opts = fixkeepchangesopts(ui, opts)
2759 opts = fixkeepchangesopts(ui, opts)
2760 if opts.get('merge'):
2760 if opts.get('merge'):
2761 if opts.get('name'):
2761 if opts.get('name'):
2762 newpath = repo.join(opts.get('name'))
2762 newpath = repo.join(opts.get('name'))
2763 else:
2763 else:
2764 newpath, i = lastsavename(q.path)
2764 newpath, i = lastsavename(q.path)
2765 if not newpath:
2765 if not newpath:
2766 ui.warn(_("no saved queues found, please use -n\n"))
2766 ui.warn(_("no saved queues found, please use -n\n"))
2767 return 1
2767 return 1
2768 mergeq = queue(ui, repo.baseui, repo.path, newpath)
2768 mergeq = queue(ui, repo.baseui, repo.path, newpath)
2769 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2769 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2770 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2770 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2771 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2771 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2772 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2772 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2773 keepchanges=opts.get('keep_changes'))
2773 keepchanges=opts.get('keep_changes'))
2774 return ret
2774 return ret
2775
2775
2776 @command("^qpop",
2776 @command("^qpop",
2777 [('a', 'all', None, _('pop all patches')),
2777 [('a', 'all', None, _('pop all patches')),
2778 ('n', 'name', '',
2778 ('n', 'name', '',
2779 _('queue name to pop (DEPRECATED)'), _('NAME')),
2779 _('queue name to pop (DEPRECATED)'), _('NAME')),
2780 ('', 'keep-changes', None,
2780 ('', 'keep-changes', None,
2781 _('tolerate non-conflicting local changes')),
2781 _('tolerate non-conflicting local changes')),
2782 ('f', 'force', None, _('forget any local changes to patched files')),
2782 ('f', 'force', None, _('forget any local changes to patched files')),
2783 ('', 'no-backup', None, _('do not save backup copies of files'))],
2783 ('', 'no-backup', None, _('do not save backup copies of files'))],
2784 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2784 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2785 def pop(ui, repo, patch=None, **opts):
2785 def pop(ui, repo, patch=None, **opts):
2786 """pop the current patch off the stack
2786 """pop the current patch off the stack
2787
2787
2788 Without argument, pops off the top of the patch stack. If given a
2788 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
2789 patch name, keeps popping off patches until the named patch is at
2790 the top of the stack.
2790 the top of the stack.
2791
2791
2792 By default, abort if the working directory contains uncommitted
2792 By default, abort if the working directory contains uncommitted
2793 changes. With --keep-changes, abort only if the uncommitted files
2793 changes. With --keep-changes, abort only if the uncommitted files
2794 overlap with patched files. With -f/--force, backup and discard
2794 overlap with patched files. With -f/--force, backup and discard
2795 changes made to such files.
2795 changes made to such files.
2796
2796
2797 Return 0 on success.
2797 Return 0 on success.
2798 """
2798 """
2799 opts = fixkeepchangesopts(ui, opts)
2799 opts = fixkeepchangesopts(ui, opts)
2800 localupdate = True
2800 localupdate = True
2801 if opts.get('name'):
2801 if opts.get('name'):
2802 q = queue(ui, repo.baseui, repo.path, repo.join(opts.get('name')))
2802 q = queue(ui, repo.baseui, repo.path, repo.join(opts.get('name')))
2803 ui.warn(_('using patch queue: %s\n') % q.path)
2803 ui.warn(_('using patch queue: %s\n') % q.path)
2804 localupdate = False
2804 localupdate = False
2805 else:
2805 else:
2806 q = repo.mq
2806 q = repo.mq
2807 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2807 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2808 all=opts.get('all'), nobackup=opts.get('no_backup'),
2808 all=opts.get('all'), nobackup=opts.get('no_backup'),
2809 keepchanges=opts.get('keep_changes'))
2809 keepchanges=opts.get('keep_changes'))
2810 q.savedirty()
2810 q.savedirty()
2811 return ret
2811 return ret
2812
2812
2813 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2813 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2814 def rename(ui, repo, patch, name=None, **opts):
2814 def rename(ui, repo, patch, name=None, **opts):
2815 """rename a patch
2815 """rename a patch
2816
2816
2817 With one argument, renames the current patch to PATCH1.
2817 With one argument, renames the current patch to PATCH1.
2818 With two arguments, renames PATCH1 to PATCH2.
2818 With two arguments, renames PATCH1 to PATCH2.
2819
2819
2820 Returns 0 on success."""
2820 Returns 0 on success."""
2821 q = repo.mq
2821 q = repo.mq
2822 if not name:
2822 if not name:
2823 name = patch
2823 name = patch
2824 patch = None
2824 patch = None
2825
2825
2826 if patch:
2826 if patch:
2827 patch = q.lookup(patch)
2827 patch = q.lookup(patch)
2828 else:
2828 else:
2829 if not q.applied:
2829 if not q.applied:
2830 ui.write(_('no patches applied\n'))
2830 ui.write(_('no patches applied\n'))
2831 return
2831 return
2832 patch = q.lookup('qtip')
2832 patch = q.lookup('qtip')
2833 absdest = q.join(name)
2833 absdest = q.join(name)
2834 if os.path.isdir(absdest):
2834 if os.path.isdir(absdest):
2835 name = normname(os.path.join(name, os.path.basename(patch)))
2835 name = normname(os.path.join(name, os.path.basename(patch)))
2836 absdest = q.join(name)
2836 absdest = q.join(name)
2837 q.checkpatchname(name)
2837 q.checkpatchname(name)
2838
2838
2839 ui.note(_('renaming %s to %s\n') % (patch, name))
2839 ui.note(_('renaming %s to %s\n') % (patch, name))
2840 i = q.findseries(patch)
2840 i = q.findseries(patch)
2841 guards = q.guard_re.findall(q.fullseries[i])
2841 guards = q.guard_re.findall(q.fullseries[i])
2842 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2842 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2843 q.parseseries()
2843 q.parseseries()
2844 q.seriesdirty = True
2844 q.seriesdirty = True
2845
2845
2846 info = q.isapplied(patch)
2846 info = q.isapplied(patch)
2847 if info:
2847 if info:
2848 q.applied[info[0]] = statusentry(info[1], name)
2848 q.applied[info[0]] = statusentry(info[1], name)
2849 q.applieddirty = True
2849 q.applieddirty = True
2850
2850
2851 destdir = os.path.dirname(absdest)
2851 destdir = os.path.dirname(absdest)
2852 if not os.path.isdir(destdir):
2852 if not os.path.isdir(destdir):
2853 os.makedirs(destdir)
2853 os.makedirs(destdir)
2854 util.rename(q.join(patch), absdest)
2854 util.rename(q.join(patch), absdest)
2855 r = q.qrepo()
2855 r = q.qrepo()
2856 if r and patch in r.dirstate:
2856 if r and patch in r.dirstate:
2857 wctx = r[None]
2857 wctx = r[None]
2858 wlock = r.wlock()
2858 wlock = r.wlock()
2859 try:
2859 try:
2860 if r.dirstate[patch] == 'a':
2860 if r.dirstate[patch] == 'a':
2861 r.dirstate.drop(patch)
2861 r.dirstate.drop(patch)
2862 r.dirstate.add(name)
2862 r.dirstate.add(name)
2863 else:
2863 else:
2864 wctx.copy(patch, name)
2864 wctx.copy(patch, name)
2865 wctx.forget([patch])
2865 wctx.forget([patch])
2866 finally:
2866 finally:
2867 wlock.release()
2867 wlock.release()
2868
2868
2869 q.savedirty()
2869 q.savedirty()
2870
2870
2871 @command("qrestore",
2871 @command("qrestore",
2872 [('d', 'delete', None, _('delete save entry')),
2872 [('d', 'delete', None, _('delete save entry')),
2873 ('u', 'update', None, _('update queue working directory'))],
2873 ('u', 'update', None, _('update queue working directory'))],
2874 _('hg qrestore [-d] [-u] REV'))
2874 _('hg qrestore [-d] [-u] REV'))
2875 def restore(ui, repo, rev, **opts):
2875 def restore(ui, repo, rev, **opts):
2876 """restore the queue state saved by a revision (DEPRECATED)
2876 """restore the queue state saved by a revision (DEPRECATED)
2877
2877
2878 This command is deprecated, use :hg:`rebase` instead."""
2878 This command is deprecated, use :hg:`rebase` instead."""
2879 rev = repo.lookup(rev)
2879 rev = repo.lookup(rev)
2880 q = repo.mq
2880 q = repo.mq
2881 q.restore(repo, rev, delete=opts.get('delete'),
2881 q.restore(repo, rev, delete=opts.get('delete'),
2882 qupdate=opts.get('update'))
2882 qupdate=opts.get('update'))
2883 q.savedirty()
2883 q.savedirty()
2884 return 0
2884 return 0
2885
2885
2886 @command("qsave",
2886 @command("qsave",
2887 [('c', 'copy', None, _('copy patch directory')),
2887 [('c', 'copy', None, _('copy patch directory')),
2888 ('n', 'name', '',
2888 ('n', 'name', '',
2889 _('copy directory name'), _('NAME')),
2889 _('copy directory name'), _('NAME')),
2890 ('e', 'empty', None, _('clear queue status file')),
2890 ('e', 'empty', None, _('clear queue status file')),
2891 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2891 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2892 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
2892 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
2893 def save(ui, repo, **opts):
2893 def save(ui, repo, **opts):
2894 """save current queue state (DEPRECATED)
2894 """save current queue state (DEPRECATED)
2895
2895
2896 This command is deprecated, use :hg:`rebase` instead."""
2896 This command is deprecated, use :hg:`rebase` instead."""
2897 q = repo.mq
2897 q = repo.mq
2898 message = cmdutil.logmessage(ui, opts)
2898 message = cmdutil.logmessage(ui, opts)
2899 ret = q.save(repo, msg=message)
2899 ret = q.save(repo, msg=message)
2900 if ret:
2900 if ret:
2901 return ret
2901 return ret
2902 q.savedirty() # save to .hg/patches before copying
2902 q.savedirty() # save to .hg/patches before copying
2903 if opts.get('copy'):
2903 if opts.get('copy'):
2904 path = q.path
2904 path = q.path
2905 if opts.get('name'):
2905 if opts.get('name'):
2906 newpath = os.path.join(q.basepath, opts.get('name'))
2906 newpath = os.path.join(q.basepath, opts.get('name'))
2907 if os.path.exists(newpath):
2907 if os.path.exists(newpath):
2908 if not os.path.isdir(newpath):
2908 if not os.path.isdir(newpath):
2909 raise util.Abort(_('destination %s exists and is not '
2909 raise util.Abort(_('destination %s exists and is not '
2910 'a directory') % newpath)
2910 'a directory') % newpath)
2911 if not opts.get('force'):
2911 if not opts.get('force'):
2912 raise util.Abort(_('destination %s exists, '
2912 raise util.Abort(_('destination %s exists, '
2913 'use -f to force') % newpath)
2913 'use -f to force') % newpath)
2914 else:
2914 else:
2915 newpath = savename(path)
2915 newpath = savename(path)
2916 ui.warn(_("copy %s to %s\n") % (path, newpath))
2916 ui.warn(_("copy %s to %s\n") % (path, newpath))
2917 util.copyfiles(path, newpath)
2917 util.copyfiles(path, newpath)
2918 if opts.get('empty'):
2918 if opts.get('empty'):
2919 del q.applied[:]
2919 del q.applied[:]
2920 q.applieddirty = True
2920 q.applieddirty = True
2921 q.savedirty()
2921 q.savedirty()
2922 return 0
2922 return 0
2923
2923
2924
2924
2925 @command("qselect",
2925 @command("qselect",
2926 [('n', 'none', None, _('disable all guards')),
2926 [('n', 'none', None, _('disable all guards')),
2927 ('s', 'series', None, _('list all guards in series file')),
2927 ('s', 'series', None, _('list all guards in series file')),
2928 ('', 'pop', None, _('pop to before first guarded applied patch')),
2928 ('', 'pop', None, _('pop to before first guarded applied patch')),
2929 ('', 'reapply', None, _('pop, then reapply patches'))],
2929 ('', 'reapply', None, _('pop, then reapply patches'))],
2930 _('hg qselect [OPTION]... [GUARD]...'))
2930 _('hg qselect [OPTION]... [GUARD]...'))
2931 def select(ui, repo, *args, **opts):
2931 def select(ui, repo, *args, **opts):
2932 '''set or print guarded patches to push
2932 '''set or print guarded patches to push
2933
2933
2934 Use the :hg:`qguard` command to set or print guards on patch, then use
2934 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
2935 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
2936 it has no guards or any positive guards match the currently
2937 selected guard, but will not be pushed if any negative guards
2937 selected guard, but will not be pushed if any negative guards
2938 match the current guard. For example::
2938 match the current guard. For example::
2939
2939
2940 qguard foo.patch -- -stable (negative guard)
2940 qguard foo.patch -- -stable (negative guard)
2941 qguard bar.patch +stable (positive guard)
2941 qguard bar.patch +stable (positive guard)
2942 qselect stable
2942 qselect stable
2943
2943
2944 This activates the "stable" guard. mq will skip foo.patch (because
2944 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
2945 it has a negative match) but push bar.patch (because it has a
2946 positive match).
2946 positive match).
2947
2947
2948 With no arguments, prints the currently active guards.
2948 With no arguments, prints the currently active guards.
2949 With one argument, sets the active guard.
2949 With one argument, sets the active guard.
2950
2950
2951 Use -n/--none to deactivate guards (no other arguments needed).
2951 Use -n/--none to deactivate guards (no other arguments needed).
2952 When no guards are active, patches with positive guards are
2952 When no guards are active, patches with positive guards are
2953 skipped and patches with negative guards are pushed.
2953 skipped and patches with negative guards are pushed.
2954
2954
2955 qselect can change the guards on applied patches. It does not pop
2955 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
2956 guarded patches by default. Use --pop to pop back to the last
2957 applied patch that is not guarded. Use --reapply (which implies
2957 applied patch that is not guarded. Use --reapply (which implies
2958 --pop) to push back to the current patch afterwards, but skip
2958 --pop) to push back to the current patch afterwards, but skip
2959 guarded patches.
2959 guarded patches.
2960
2960
2961 Use -s/--series to print a list of all guards in the series file
2961 Use -s/--series to print a list of all guards in the series file
2962 (no other arguments needed). Use -v for more information.
2962 (no other arguments needed). Use -v for more information.
2963
2963
2964 Returns 0 on success.'''
2964 Returns 0 on success.'''
2965
2965
2966 q = repo.mq
2966 q = repo.mq
2967 guards = q.active()
2967 guards = q.active()
2968 if args or opts.get('none'):
2968 if args or opts.get('none'):
2969 old_unapplied = q.unapplied(repo)
2969 old_unapplied = q.unapplied(repo)
2970 old_guarded = [i for i in xrange(len(q.applied)) if
2970 old_guarded = [i for i in xrange(len(q.applied)) if
2971 not q.pushable(i)[0]]
2971 not q.pushable(i)[0]]
2972 q.setactive(args)
2972 q.setactive(args)
2973 q.savedirty()
2973 q.savedirty()
2974 if not args:
2974 if not args:
2975 ui.status(_('guards deactivated\n'))
2975 ui.status(_('guards deactivated\n'))
2976 if not opts.get('pop') and not opts.get('reapply'):
2976 if not opts.get('pop') and not opts.get('reapply'):
2977 unapplied = q.unapplied(repo)
2977 unapplied = q.unapplied(repo)
2978 guarded = [i for i in xrange(len(q.applied))
2978 guarded = [i for i in xrange(len(q.applied))
2979 if not q.pushable(i)[0]]
2979 if not q.pushable(i)[0]]
2980 if len(unapplied) != len(old_unapplied):
2980 if len(unapplied) != len(old_unapplied):
2981 ui.status(_('number of unguarded, unapplied patches has '
2981 ui.status(_('number of unguarded, unapplied patches has '
2982 'changed from %d to %d\n') %
2982 'changed from %d to %d\n') %
2983 (len(old_unapplied), len(unapplied)))
2983 (len(old_unapplied), len(unapplied)))
2984 if len(guarded) != len(old_guarded):
2984 if len(guarded) != len(old_guarded):
2985 ui.status(_('number of guarded, applied patches has changed '
2985 ui.status(_('number of guarded, applied patches has changed '
2986 'from %d to %d\n') %
2986 'from %d to %d\n') %
2987 (len(old_guarded), len(guarded)))
2987 (len(old_guarded), len(guarded)))
2988 elif opts.get('series'):
2988 elif opts.get('series'):
2989 guards = {}
2989 guards = {}
2990 noguards = 0
2990 noguards = 0
2991 for gs in q.seriesguards:
2991 for gs in q.seriesguards:
2992 if not gs:
2992 if not gs:
2993 noguards += 1
2993 noguards += 1
2994 for g in gs:
2994 for g in gs:
2995 guards.setdefault(g, 0)
2995 guards.setdefault(g, 0)
2996 guards[g] += 1
2996 guards[g] += 1
2997 if ui.verbose:
2997 if ui.verbose:
2998 guards['NONE'] = noguards
2998 guards['NONE'] = noguards
2999 guards = guards.items()
2999 guards = guards.items()
3000 guards.sort(key=lambda x: x[0][1:])
3000 guards.sort(key=lambda x: x[0][1:])
3001 if guards:
3001 if guards:
3002 ui.note(_('guards in series file:\n'))
3002 ui.note(_('guards in series file:\n'))
3003 for guard, count in guards:
3003 for guard, count in guards:
3004 ui.note('%2d ' % count)
3004 ui.note('%2d ' % count)
3005 ui.write(guard, '\n')
3005 ui.write(guard, '\n')
3006 else:
3006 else:
3007 ui.note(_('no guards in series file\n'))
3007 ui.note(_('no guards in series file\n'))
3008 else:
3008 else:
3009 if guards:
3009 if guards:
3010 ui.note(_('active guards:\n'))
3010 ui.note(_('active guards:\n'))
3011 for g in guards:
3011 for g in guards:
3012 ui.write(g, '\n')
3012 ui.write(g, '\n')
3013 else:
3013 else:
3014 ui.write(_('no active guards\n'))
3014 ui.write(_('no active guards\n'))
3015 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
3015 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
3016 popped = False
3016 popped = False
3017 if opts.get('pop') or opts.get('reapply'):
3017 if opts.get('pop') or opts.get('reapply'):
3018 for i in xrange(len(q.applied)):
3018 for i in xrange(len(q.applied)):
3019 pushable, reason = q.pushable(i)
3019 pushable, reason = q.pushable(i)
3020 if not pushable:
3020 if not pushable:
3021 ui.status(_('popping guarded patches\n'))
3021 ui.status(_('popping guarded patches\n'))
3022 popped = True
3022 popped = True
3023 if i == 0:
3023 if i == 0:
3024 q.pop(repo, all=True)
3024 q.pop(repo, all=True)
3025 else:
3025 else:
3026 q.pop(repo, str(i - 1))
3026 q.pop(repo, str(i - 1))
3027 break
3027 break
3028 if popped:
3028 if popped:
3029 try:
3029 try:
3030 if reapply:
3030 if reapply:
3031 ui.status(_('reapplying unguarded patches\n'))
3031 ui.status(_('reapplying unguarded patches\n'))
3032 q.push(repo, reapply)
3032 q.push(repo, reapply)
3033 finally:
3033 finally:
3034 q.savedirty()
3034 q.savedirty()
3035
3035
3036 @command("qfinish",
3036 @command("qfinish",
3037 [('a', 'applied', None, _('finish all applied changesets'))],
3037 [('a', 'applied', None, _('finish all applied changesets'))],
3038 _('hg qfinish [-a] [REV]...'))
3038 _('hg qfinish [-a] [REV]...'))
3039 def finish(ui, repo, *revrange, **opts):
3039 def finish(ui, repo, *revrange, **opts):
3040 """move applied patches into repository history
3040 """move applied patches into repository history
3041
3041
3042 Finishes the specified revisions (corresponding to applied
3042 Finishes the specified revisions (corresponding to applied
3043 patches) by moving them out of mq control into regular repository
3043 patches) by moving them out of mq control into regular repository
3044 history.
3044 history.
3045
3045
3046 Accepts a revision range or the -a/--applied option. If --applied
3046 Accepts a revision range or the -a/--applied option. If --applied
3047 is specified, all applied mq revisions are removed from mq
3047 is specified, all applied mq revisions are removed from mq
3048 control. Otherwise, the given revisions must be at the base of the
3048 control. Otherwise, the given revisions must be at the base of the
3049 stack of applied patches.
3049 stack of applied patches.
3050
3050
3051 This can be especially useful if your changes have been applied to
3051 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
3052 an upstream repository, or if you are about to push your changes
3053 to upstream.
3053 to upstream.
3054
3054
3055 Returns 0 on success.
3055 Returns 0 on success.
3056 """
3056 """
3057 if not opts.get('applied') and not revrange:
3057 if not opts.get('applied') and not revrange:
3058 raise util.Abort(_('no revisions specified'))
3058 raise util.Abort(_('no revisions specified'))
3059 elif opts.get('applied'):
3059 elif opts.get('applied'):
3060 revrange = ('qbase::qtip',) + revrange
3060 revrange = ('qbase::qtip',) + revrange
3061
3061
3062 q = repo.mq
3062 q = repo.mq
3063 if not q.applied:
3063 if not q.applied:
3064 ui.status(_('no patches applied\n'))
3064 ui.status(_('no patches applied\n'))
3065 return 0
3065 return 0
3066
3066
3067 revs = scmutil.revrange(repo, revrange)
3067 revs = scmutil.revrange(repo, revrange)
3068 if repo['.'].rev() in revs and repo[None].files():
3068 if repo['.'].rev() in revs and repo[None].files():
3069 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3069 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3070 # queue.finish may changes phases but leave the responsibility to lock the
3070 # 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
3071 # repo to the caller to avoid deadlock with wlock. This command code is
3072 # responsibility for this locking.
3072 # responsibility for this locking.
3073 lock = repo.lock()
3073 lock = repo.lock()
3074 try:
3074 try:
3075 q.finish(repo, revs)
3075 q.finish(repo, revs)
3076 q.savedirty()
3076 q.savedirty()
3077 finally:
3077 finally:
3078 lock.release()
3078 lock.release()
3079 return 0
3079 return 0
3080
3080
3081 @command("qqueue",
3081 @command("qqueue",
3082 [('l', 'list', False, _('list all available queues')),
3082 [('l', 'list', False, _('list all available queues')),
3083 ('', 'active', False, _('print name of active queue')),
3083 ('', 'active', False, _('print name of active queue')),
3084 ('c', 'create', False, _('create new queue')),
3084 ('c', 'create', False, _('create new queue')),
3085 ('', 'rename', False, _('rename active queue')),
3085 ('', 'rename', False, _('rename active queue')),
3086 ('', 'delete', False, _('delete reference to queue')),
3086 ('', 'delete', False, _('delete reference to queue')),
3087 ('', 'purge', False, _('delete queue, and remove patch dir')),
3087 ('', 'purge', False, _('delete queue, and remove patch dir')),
3088 ],
3088 ],
3089 _('[OPTION] [QUEUE]'))
3089 _('[OPTION] [QUEUE]'))
3090 def qqueue(ui, repo, name=None, **opts):
3090 def qqueue(ui, repo, name=None, **opts):
3091 '''manage multiple patch queues
3091 '''manage multiple patch queues
3092
3092
3093 Supports switching between different patch queues, as well as creating
3093 Supports switching between different patch queues, as well as creating
3094 new patch queues and deleting existing ones.
3094 new patch queues and deleting existing ones.
3095
3095
3096 Omitting a queue name or specifying -l/--list will show you the registered
3096 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
3097 queues - by default the "normal" patches queue is registered. The currently
3098 active queue will be marked with "(active)". Specifying --active will print
3098 active queue will be marked with "(active)". Specifying --active will print
3099 only the name of the active queue.
3099 only the name of the active queue.
3100
3100
3101 To create a new queue, use -c/--create. The queue is automatically made
3101 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
3102 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
3103 currently active queue in the repository. Then the queue will only be
3104 created and switching will fail.
3104 created and switching will fail.
3105
3105
3106 To delete an existing queue, use --delete. You cannot delete the currently
3106 To delete an existing queue, use --delete. You cannot delete the currently
3107 active queue.
3107 active queue.
3108
3108
3109 Returns 0 on success.
3109 Returns 0 on success.
3110 '''
3110 '''
3111 q = repo.mq
3111 q = repo.mq
3112 _defaultqueue = 'patches'
3112 _defaultqueue = 'patches'
3113 _allqueues = 'patches.queues'
3113 _allqueues = 'patches.queues'
3114 _activequeue = 'patches.queue'
3114 _activequeue = 'patches.queue'
3115
3115
3116 def _getcurrent():
3116 def _getcurrent():
3117 cur = os.path.basename(q.path)
3117 cur = os.path.basename(q.path)
3118 if cur.startswith('patches-'):
3118 if cur.startswith('patches-'):
3119 cur = cur[8:]
3119 cur = cur[8:]
3120 return cur
3120 return cur
3121
3121
3122 def _noqueues():
3122 def _noqueues():
3123 try:
3123 try:
3124 fh = repo.opener(_allqueues, 'r')
3124 fh = repo.opener(_allqueues, 'r')
3125 fh.close()
3125 fh.close()
3126 except IOError:
3126 except IOError:
3127 return True
3127 return True
3128
3128
3129 return False
3129 return False
3130
3130
3131 def _getqueues():
3131 def _getqueues():
3132 current = _getcurrent()
3132 current = _getcurrent()
3133
3133
3134 try:
3134 try:
3135 fh = repo.opener(_allqueues, 'r')
3135 fh = repo.opener(_allqueues, 'r')
3136 queues = [queue.strip() for queue in fh if queue.strip()]
3136 queues = [queue.strip() for queue in fh if queue.strip()]
3137 fh.close()
3137 fh.close()
3138 if current not in queues:
3138 if current not in queues:
3139 queues.append(current)
3139 queues.append(current)
3140 except IOError:
3140 except IOError:
3141 queues = [_defaultqueue]
3141 queues = [_defaultqueue]
3142
3142
3143 return sorted(queues)
3143 return sorted(queues)
3144
3144
3145 def _setactive(name):
3145 def _setactive(name):
3146 if q.applied:
3146 if q.applied:
3147 raise util.Abort(_('new queue created, but cannot make active '
3147 raise util.Abort(_('new queue created, but cannot make active '
3148 'as patches are applied'))
3148 'as patches are applied'))
3149 _setactivenocheck(name)
3149 _setactivenocheck(name)
3150
3150
3151 def _setactivenocheck(name):
3151 def _setactivenocheck(name):
3152 fh = repo.opener(_activequeue, 'w')
3152 fh = repo.opener(_activequeue, 'w')
3153 if name != 'patches':
3153 if name != 'patches':
3154 fh.write(name)
3154 fh.write(name)
3155 fh.close()
3155 fh.close()
3156
3156
3157 def _addqueue(name):
3157 def _addqueue(name):
3158 fh = repo.opener(_allqueues, 'a')
3158 fh = repo.opener(_allqueues, 'a')
3159 fh.write('%s\n' % (name,))
3159 fh.write('%s\n' % (name,))
3160 fh.close()
3160 fh.close()
3161
3161
3162 def _queuedir(name):
3162 def _queuedir(name):
3163 if name == 'patches':
3163 if name == 'patches':
3164 return repo.join('patches')
3164 return repo.join('patches')
3165 else:
3165 else:
3166 return repo.join('patches-' + name)
3166 return repo.join('patches-' + name)
3167
3167
3168 def _validname(name):
3168 def _validname(name):
3169 for n in name:
3169 for n in name:
3170 if n in ':\\/.':
3170 if n in ':\\/.':
3171 return False
3171 return False
3172 return True
3172 return True
3173
3173
3174 def _delete(name):
3174 def _delete(name):
3175 if name not in existing:
3175 if name not in existing:
3176 raise util.Abort(_('cannot delete queue that does not exist'))
3176 raise util.Abort(_('cannot delete queue that does not exist'))
3177
3177
3178 current = _getcurrent()
3178 current = _getcurrent()
3179
3179
3180 if name == current:
3180 if name == current:
3181 raise util.Abort(_('cannot delete currently active queue'))
3181 raise util.Abort(_('cannot delete currently active queue'))
3182
3182
3183 fh = repo.opener('patches.queues.new', 'w')
3183 fh = repo.opener('patches.queues.new', 'w')
3184 for queue in existing:
3184 for queue in existing:
3185 if queue == name:
3185 if queue == name:
3186 continue
3186 continue
3187 fh.write('%s\n' % (queue,))
3187 fh.write('%s\n' % (queue,))
3188 fh.close()
3188 fh.close()
3189 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3189 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3190
3190
3191 if not name or opts.get('list') or opts.get('active'):
3191 if not name or opts.get('list') or opts.get('active'):
3192 current = _getcurrent()
3192 current = _getcurrent()
3193 if opts.get('active'):
3193 if opts.get('active'):
3194 ui.write('%s\n' % (current,))
3194 ui.write('%s\n' % (current,))
3195 return
3195 return
3196 for queue in _getqueues():
3196 for queue in _getqueues():
3197 ui.write('%s' % (queue,))
3197 ui.write('%s' % (queue,))
3198 if queue == current and not ui.quiet:
3198 if queue == current and not ui.quiet:
3199 ui.write(_(' (active)\n'))
3199 ui.write(_(' (active)\n'))
3200 else:
3200 else:
3201 ui.write('\n')
3201 ui.write('\n')
3202 return
3202 return
3203
3203
3204 if not _validname(name):
3204 if not _validname(name):
3205 raise util.Abort(
3205 raise util.Abort(
3206 _('invalid queue name, may not contain the characters ":\\/."'))
3206 _('invalid queue name, may not contain the characters ":\\/."'))
3207
3207
3208 existing = _getqueues()
3208 existing = _getqueues()
3209
3209
3210 if opts.get('create'):
3210 if opts.get('create'):
3211 if name in existing:
3211 if name in existing:
3212 raise util.Abort(_('queue "%s" already exists') % name)
3212 raise util.Abort(_('queue "%s" already exists') % name)
3213 if _noqueues():
3213 if _noqueues():
3214 _addqueue(_defaultqueue)
3214 _addqueue(_defaultqueue)
3215 _addqueue(name)
3215 _addqueue(name)
3216 _setactive(name)
3216 _setactive(name)
3217 elif opts.get('rename'):
3217 elif opts.get('rename'):
3218 current = _getcurrent()
3218 current = _getcurrent()
3219 if name == current:
3219 if name == current:
3220 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
3220 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
3221 if name in existing:
3221 if name in existing:
3222 raise util.Abort(_('queue "%s" already exists') % name)
3222 raise util.Abort(_('queue "%s" already exists') % name)
3223
3223
3224 olddir = _queuedir(current)
3224 olddir = _queuedir(current)
3225 newdir = _queuedir(name)
3225 newdir = _queuedir(name)
3226
3226
3227 if os.path.exists(newdir):
3227 if os.path.exists(newdir):
3228 raise util.Abort(_('non-queue directory "%s" already exists') %
3228 raise util.Abort(_('non-queue directory "%s" already exists') %
3229 newdir)
3229 newdir)
3230
3230
3231 fh = repo.opener('patches.queues.new', 'w')
3231 fh = repo.opener('patches.queues.new', 'w')
3232 for queue in existing:
3232 for queue in existing:
3233 if queue == current:
3233 if queue == current:
3234 fh.write('%s\n' % (name,))
3234 fh.write('%s\n' % (name,))
3235 if os.path.exists(olddir):
3235 if os.path.exists(olddir):
3236 util.rename(olddir, newdir)
3236 util.rename(olddir, newdir)
3237 else:
3237 else:
3238 fh.write('%s\n' % (queue,))
3238 fh.write('%s\n' % (queue,))
3239 fh.close()
3239 fh.close()
3240 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3240 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3241 _setactivenocheck(name)
3241 _setactivenocheck(name)
3242 elif opts.get('delete'):
3242 elif opts.get('delete'):
3243 _delete(name)
3243 _delete(name)
3244 elif opts.get('purge'):
3244 elif opts.get('purge'):
3245 if name in existing:
3245 if name in existing:
3246 _delete(name)
3246 _delete(name)
3247 qdir = _queuedir(name)
3247 qdir = _queuedir(name)
3248 if os.path.exists(qdir):
3248 if os.path.exists(qdir):
3249 shutil.rmtree(qdir)
3249 shutil.rmtree(qdir)
3250 else:
3250 else:
3251 if name not in existing:
3251 if name not in existing:
3252 raise util.Abort(_('use --create to create a new queue'))
3252 raise util.Abort(_('use --create to create a new queue'))
3253 _setactive(name)
3253 _setactive(name)
3254
3254
3255 def mqphasedefaults(repo, roots):
3255 def mqphasedefaults(repo, roots):
3256 """callback used to set mq changeset as secret when no phase data exists"""
3256 """callback used to set mq changeset as secret when no phase data exists"""
3257 if repo.mq.applied:
3257 if repo.mq.applied:
3258 if repo.ui.configbool('mq', 'secret', False):
3258 if repo.ui.configbool('mq', 'secret', False):
3259 mqphase = phases.secret
3259 mqphase = phases.secret
3260 else:
3260 else:
3261 mqphase = phases.draft
3261 mqphase = phases.draft
3262 qbase = repo[repo.mq.applied[0].node]
3262 qbase = repo[repo.mq.applied[0].node]
3263 roots[mqphase].add(qbase.node())
3263 roots[mqphase].add(qbase.node())
3264 return roots
3264 return roots
3265
3265
3266 def reposetup(ui, repo):
3266 def reposetup(ui, repo):
3267 class mqrepo(repo.__class__):
3267 class mqrepo(repo.__class__):
3268 @localrepo.unfilteredpropertycache
3268 @localrepo.unfilteredpropertycache
3269 def mq(self):
3269 def mq(self):
3270 return queue(self.ui, self.baseui, self.path)
3270 return queue(self.ui, self.baseui, self.path)
3271
3271
3272 def invalidateall(self):
3272 def invalidateall(self):
3273 super(mqrepo, self).invalidateall()
3273 super(mqrepo, self).invalidateall()
3274 if localrepo.hasunfilteredcache(self, 'mq'):
3274 if localrepo.hasunfilteredcache(self, 'mq'):
3275 self.mq.invalidate()
3275 # recreate mq in case queue path was changed
3276 delattr(self.unfiltered(), 'mq')
3276
3277
3277 def abortifwdirpatched(self, errmsg, force=False):
3278 def abortifwdirpatched(self, errmsg, force=False):
3278 if self.mq.applied and self.mq.checkapplied and not force:
3279 if self.mq.applied and self.mq.checkapplied and not force:
3279 parents = self.dirstate.parents()
3280 parents = self.dirstate.parents()
3280 patches = [s.node for s in self.mq.applied]
3281 patches = [s.node for s in self.mq.applied]
3281 if parents[0] in patches or parents[1] in patches:
3282 if parents[0] in patches or parents[1] in patches:
3282 raise util.Abort(errmsg)
3283 raise util.Abort(errmsg)
3283
3284
3284 def commit(self, text="", user=None, date=None, match=None,
3285 def commit(self, text="", user=None, date=None, match=None,
3285 force=False, editor=False, extra={}):
3286 force=False, editor=False, extra={}):
3286 self.abortifwdirpatched(
3287 self.abortifwdirpatched(
3287 _('cannot commit over an applied mq patch'),
3288 _('cannot commit over an applied mq patch'),
3288 force)
3289 force)
3289
3290
3290 return super(mqrepo, self).commit(text, user, date, match, force,
3291 return super(mqrepo, self).commit(text, user, date, match, force,
3291 editor, extra)
3292 editor, extra)
3292
3293
3293 def checkpush(self, force, revs):
3294 def checkpush(self, force, revs):
3294 if self.mq.applied and self.mq.checkapplied and not force:
3295 if self.mq.applied and self.mq.checkapplied and not force:
3295 outapplied = [e.node for e in self.mq.applied]
3296 outapplied = [e.node for e in self.mq.applied]
3296 if revs:
3297 if revs:
3297 # Assume applied patches have no non-patch descendants and
3298 # Assume applied patches have no non-patch descendants and
3298 # are not on remote already. Filtering any changeset not
3299 # are not on remote already. Filtering any changeset not
3299 # pushed.
3300 # pushed.
3300 heads = set(revs)
3301 heads = set(revs)
3301 for node in reversed(outapplied):
3302 for node in reversed(outapplied):
3302 if node in heads:
3303 if node in heads:
3303 break
3304 break
3304 else:
3305 else:
3305 outapplied.pop()
3306 outapplied.pop()
3306 # looking for pushed and shared changeset
3307 # looking for pushed and shared changeset
3307 for node in outapplied:
3308 for node in outapplied:
3308 if self[node].phase() < phases.secret:
3309 if self[node].phase() < phases.secret:
3309 raise util.Abort(_('source has mq patches applied'))
3310 raise util.Abort(_('source has mq patches applied'))
3310 # no non-secret patches pushed
3311 # no non-secret patches pushed
3311 super(mqrepo, self).checkpush(force, revs)
3312 super(mqrepo, self).checkpush(force, revs)
3312
3313
3313 def _findtags(self):
3314 def _findtags(self):
3314 '''augment tags from base class with patch tags'''
3315 '''augment tags from base class with patch tags'''
3315 result = super(mqrepo, self)._findtags()
3316 result = super(mqrepo, self)._findtags()
3316
3317
3317 q = self.mq
3318 q = self.mq
3318 if not q.applied:
3319 if not q.applied:
3319 return result
3320 return result
3320
3321
3321 mqtags = [(patch.node, patch.name) for patch in q.applied]
3322 mqtags = [(patch.node, patch.name) for patch in q.applied]
3322
3323
3323 try:
3324 try:
3324 # for now ignore filtering business
3325 # for now ignore filtering business
3325 self.unfiltered().changelog.rev(mqtags[-1][0])
3326 self.unfiltered().changelog.rev(mqtags[-1][0])
3326 except error.LookupError:
3327 except error.LookupError:
3327 self.ui.warn(_('mq status file refers to unknown node %s\n')
3328 self.ui.warn(_('mq status file refers to unknown node %s\n')
3328 % short(mqtags[-1][0]))
3329 % short(mqtags[-1][0]))
3329 return result
3330 return result
3330
3331
3331 # do not add fake tags for filtered revisions
3332 # do not add fake tags for filtered revisions
3332 included = self.changelog.hasnode
3333 included = self.changelog.hasnode
3333 mqtags = [mqt for mqt in mqtags if included(mqt[0])]
3334 mqtags = [mqt for mqt in mqtags if included(mqt[0])]
3334 if not mqtags:
3335 if not mqtags:
3335 return result
3336 return result
3336
3337
3337 mqtags.append((mqtags[-1][0], 'qtip'))
3338 mqtags.append((mqtags[-1][0], 'qtip'))
3338 mqtags.append((mqtags[0][0], 'qbase'))
3339 mqtags.append((mqtags[0][0], 'qbase'))
3339 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3340 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3340 tags = result[0]
3341 tags = result[0]
3341 for patch in mqtags:
3342 for patch in mqtags:
3342 if patch[1] in tags:
3343 if patch[1] in tags:
3343 self.ui.warn(_('tag %s overrides mq patch of the same '
3344 self.ui.warn(_('tag %s overrides mq patch of the same '
3344 'name\n') % patch[1])
3345 'name\n') % patch[1])
3345 else:
3346 else:
3346 tags[patch[1]] = patch[0]
3347 tags[patch[1]] = patch[0]
3347
3348
3348 return result
3349 return result
3349
3350
3350 if repo.local():
3351 if repo.local():
3351 repo.__class__ = mqrepo
3352 repo.__class__ = mqrepo
3352
3353
3353 repo._phasedefaults.append(mqphasedefaults)
3354 repo._phasedefaults.append(mqphasedefaults)
3354
3355
3355 def mqimport(orig, ui, repo, *args, **kwargs):
3356 def mqimport(orig, ui, repo, *args, **kwargs):
3356 if (util.safehasattr(repo, 'abortifwdirpatched')
3357 if (util.safehasattr(repo, 'abortifwdirpatched')
3357 and not kwargs.get('no_commit', False)):
3358 and not kwargs.get('no_commit', False)):
3358 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3359 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3359 kwargs.get('force'))
3360 kwargs.get('force'))
3360 return orig(ui, repo, *args, **kwargs)
3361 return orig(ui, repo, *args, **kwargs)
3361
3362
3362 def mqinit(orig, ui, *args, **kwargs):
3363 def mqinit(orig, ui, *args, **kwargs):
3363 mq = kwargs.pop('mq', None)
3364 mq = kwargs.pop('mq', None)
3364
3365
3365 if not mq:
3366 if not mq:
3366 return orig(ui, *args, **kwargs)
3367 return orig(ui, *args, **kwargs)
3367
3368
3368 if args:
3369 if args:
3369 repopath = args[0]
3370 repopath = args[0]
3370 if not hg.islocal(repopath):
3371 if not hg.islocal(repopath):
3371 raise util.Abort(_('only a local queue repository '
3372 raise util.Abort(_('only a local queue repository '
3372 'may be initialized'))
3373 'may be initialized'))
3373 else:
3374 else:
3374 repopath = cmdutil.findrepo(os.getcwd())
3375 repopath = cmdutil.findrepo(os.getcwd())
3375 if not repopath:
3376 if not repopath:
3376 raise util.Abort(_('there is no Mercurial repository here '
3377 raise util.Abort(_('there is no Mercurial repository here '
3377 '(.hg not found)'))
3378 '(.hg not found)'))
3378 repo = hg.repository(ui, repopath)
3379 repo = hg.repository(ui, repopath)
3379 return qinit(ui, repo, True)
3380 return qinit(ui, repo, True)
3380
3381
3381 def mqcommand(orig, ui, repo, *args, **kwargs):
3382 def mqcommand(orig, ui, repo, *args, **kwargs):
3382 """Add --mq option to operate on patch repository instead of main"""
3383 """Add --mq option to operate on patch repository instead of main"""
3383
3384
3384 # some commands do not like getting unknown options
3385 # some commands do not like getting unknown options
3385 mq = kwargs.pop('mq', None)
3386 mq = kwargs.pop('mq', None)
3386
3387
3387 if not mq:
3388 if not mq:
3388 return orig(ui, repo, *args, **kwargs)
3389 return orig(ui, repo, *args, **kwargs)
3389
3390
3390 q = repo.mq
3391 q = repo.mq
3391 r = q.qrepo()
3392 r = q.qrepo()
3392 if not r:
3393 if not r:
3393 raise util.Abort(_('no queue repository'))
3394 raise util.Abort(_('no queue repository'))
3394 return orig(r.ui, r, *args, **kwargs)
3395 return orig(r.ui, r, *args, **kwargs)
3395
3396
3396 def summaryhook(ui, repo):
3397 def summaryhook(ui, repo):
3397 q = repo.mq
3398 q = repo.mq
3398 m = []
3399 m = []
3399 a, u = len(q.applied), len(q.unapplied(repo))
3400 a, u = len(q.applied), len(q.unapplied(repo))
3400 if a:
3401 if a:
3401 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3402 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3402 if u:
3403 if u:
3403 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3404 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3404 if m:
3405 if m:
3405 # i18n: column positioning for "hg summary"
3406 # i18n: column positioning for "hg summary"
3406 ui.write(_("mq: %s\n") % ', '.join(m))
3407 ui.write(_("mq: %s\n") % ', '.join(m))
3407 else:
3408 else:
3408 # i18n: column positioning for "hg summary"
3409 # i18n: column positioning for "hg summary"
3409 ui.note(_("mq: (empty queue)\n"))
3410 ui.note(_("mq: (empty queue)\n"))
3410
3411
3411 def revsetmq(repo, subset, x):
3412 def revsetmq(repo, subset, x):
3412 """``mq()``
3413 """``mq()``
3413 Changesets managed by MQ.
3414 Changesets managed by MQ.
3414 """
3415 """
3415 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3416 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3416 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3417 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3417 return revset.baseset([r for r in subset if r in applied])
3418 return revset.baseset([r for r in subset if r in applied])
3418
3419
3419 # tell hggettext to extract docstrings from these functions:
3420 # tell hggettext to extract docstrings from these functions:
3420 i18nfunctions = [revsetmq]
3421 i18nfunctions = [revsetmq]
3421
3422
3422 def extsetup(ui):
3423 def extsetup(ui):
3423 # Ensure mq wrappers are called first, regardless of extension load order by
3424 # Ensure mq wrappers are called first, regardless of extension load order by
3424 # NOT wrapping in uisetup() and instead deferring to init stage two here.
3425 # NOT wrapping in uisetup() and instead deferring to init stage two here.
3425 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3426 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3426
3427
3427 extensions.wrapcommand(commands.table, 'import', mqimport)
3428 extensions.wrapcommand(commands.table, 'import', mqimport)
3428 cmdutil.summaryhooks.add('mq', summaryhook)
3429 cmdutil.summaryhooks.add('mq', summaryhook)
3429
3430
3430 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3431 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3431 entry[1].extend(mqopt)
3432 entry[1].extend(mqopt)
3432
3433
3433 nowrap = set(commands.norepo.split(" "))
3434 nowrap = set(commands.norepo.split(" "))
3434
3435
3435 def dotable(cmdtable):
3436 def dotable(cmdtable):
3436 for cmd in cmdtable.keys():
3437 for cmd in cmdtable.keys():
3437 cmd = cmdutil.parsealiases(cmd)[0]
3438 cmd = cmdutil.parsealiases(cmd)[0]
3438 if cmd in nowrap:
3439 if cmd in nowrap:
3439 continue
3440 continue
3440 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3441 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3441 entry[1].extend(mqopt)
3442 entry[1].extend(mqopt)
3442
3443
3443 dotable(commands.table)
3444 dotable(commands.table)
3444
3445
3445 for extname, extmodule in extensions.extensions():
3446 for extname, extmodule in extensions.extensions():
3446 if extmodule.__file__ != __file__:
3447 if extmodule.__file__ != __file__:
3447 dotable(getattr(extmodule, 'cmdtable', {}))
3448 dotable(getattr(extmodule, 'cmdtable', {}))
3448
3449
3449 revset.symbols['mq'] = revsetmq
3450 revset.symbols['mq'] = revsetmq
3450
3451
3451 colortable = {'qguard.negative': 'red',
3452 colortable = {'qguard.negative': 'red',
3452 'qguard.positive': 'yellow',
3453 'qguard.positive': 'yellow',
3453 'qguard.unguarded': 'green',
3454 'qguard.unguarded': 'green',
3454 'qseries.applied': 'blue bold underline',
3455 'qseries.applied': 'blue bold underline',
3455 'qseries.guarded': 'black bold',
3456 'qseries.guarded': 'black bold',
3456 'qseries.missing': 'red bold',
3457 'qseries.missing': 'red bold',
3457 'qseries.unapplied': 'black bold'}
3458 'qseries.unapplied': 'black bold'}
3458
3459
3459 commands.inferrepo += " qnew qrefresh qdiff qcommit"
3460 commands.inferrepo += " qnew qrefresh qdiff qcommit"
@@ -1,319 +1,324 b''
1 import sys, os, struct, subprocess, cStringIO, re, shutil
1 import sys, os, struct, subprocess, cStringIO, re, shutil
2
2
3 def connect(path=None):
3 def connect(path=None):
4 cmdline = ['hg', 'serve', '--cmdserver', 'pipe']
4 cmdline = ['hg', 'serve', '--cmdserver', 'pipe']
5 if path:
5 if path:
6 cmdline += ['-R', path]
6 cmdline += ['-R', path]
7
7
8 server = subprocess.Popen(cmdline, stdin=subprocess.PIPE,
8 server = subprocess.Popen(cmdline, stdin=subprocess.PIPE,
9 stdout=subprocess.PIPE)
9 stdout=subprocess.PIPE)
10
10
11 return server
11 return server
12
12
13 def writeblock(server, data):
13 def writeblock(server, data):
14 server.stdin.write(struct.pack('>I', len(data)))
14 server.stdin.write(struct.pack('>I', len(data)))
15 server.stdin.write(data)
15 server.stdin.write(data)
16 server.stdin.flush()
16 server.stdin.flush()
17
17
18 def readchannel(server):
18 def readchannel(server):
19 data = server.stdout.read(5)
19 data = server.stdout.read(5)
20 if not data:
20 if not data:
21 raise EOFError
21 raise EOFError
22 channel, length = struct.unpack('>cI', data)
22 channel, length = struct.unpack('>cI', data)
23 if channel in 'IL':
23 if channel in 'IL':
24 return channel, length
24 return channel, length
25 else:
25 else:
26 return channel, server.stdout.read(length)
26 return channel, server.stdout.read(length)
27
27
28 def sep(text):
28 def sep(text):
29 return text.replace('\\', '/')
29 return text.replace('\\', '/')
30
30
31 def runcommand(server, args, output=sys.stdout, error=sys.stderr, input=None,
31 def runcommand(server, args, output=sys.stdout, error=sys.stderr, input=None,
32 outfilter=lambda x: x):
32 outfilter=lambda x: x):
33 print ' runcommand', ' '.join(args)
33 print ' runcommand', ' '.join(args)
34 sys.stdout.flush()
34 sys.stdout.flush()
35 server.stdin.write('runcommand\n')
35 server.stdin.write('runcommand\n')
36 writeblock(server, '\0'.join(args))
36 writeblock(server, '\0'.join(args))
37
37
38 if not input:
38 if not input:
39 input = cStringIO.StringIO()
39 input = cStringIO.StringIO()
40
40
41 while True:
41 while True:
42 ch, data = readchannel(server)
42 ch, data = readchannel(server)
43 if ch == 'o':
43 if ch == 'o':
44 output.write(outfilter(data))
44 output.write(outfilter(data))
45 output.flush()
45 output.flush()
46 elif ch == 'e':
46 elif ch == 'e':
47 error.write(data)
47 error.write(data)
48 error.flush()
48 error.flush()
49 elif ch == 'I':
49 elif ch == 'I':
50 writeblock(server, input.read(data))
50 writeblock(server, input.read(data))
51 elif ch == 'L':
51 elif ch == 'L':
52 writeblock(server, input.readline(data))
52 writeblock(server, input.readline(data))
53 elif ch == 'r':
53 elif ch == 'r':
54 return struct.unpack('>i', data)[0]
54 return struct.unpack('>i', data)[0]
55 else:
55 else:
56 print "unexpected channel %c: %r" % (ch, data)
56 print "unexpected channel %c: %r" % (ch, data)
57 if ch.isupper():
57 if ch.isupper():
58 return
58 return
59
59
60 def check(func, repopath=None):
60 def check(func, repopath=None):
61 print
61 print
62 print 'testing %s:' % func.__name__
62 print 'testing %s:' % func.__name__
63 print
63 print
64 sys.stdout.flush()
64 sys.stdout.flush()
65 server = connect(repopath)
65 server = connect(repopath)
66 try:
66 try:
67 return func(server)
67 return func(server)
68 finally:
68 finally:
69 server.stdin.close()
69 server.stdin.close()
70 server.wait()
70 server.wait()
71
71
72 def unknowncommand(server):
72 def unknowncommand(server):
73 server.stdin.write('unknowncommand\n')
73 server.stdin.write('unknowncommand\n')
74
74
75 def hellomessage(server):
75 def hellomessage(server):
76 ch, data = readchannel(server)
76 ch, data = readchannel(server)
77 # escaping python tests output not supported
77 # escaping python tests output not supported
78 print '%c, %r' % (ch, re.sub('encoding: [a-zA-Z0-9-]+', 'encoding: ***',
78 print '%c, %r' % (ch, re.sub('encoding: [a-zA-Z0-9-]+', 'encoding: ***',
79 data))
79 data))
80
80
81 # run an arbitrary command to make sure the next thing the server sends
81 # run an arbitrary command to make sure the next thing the server sends
82 # isn't part of the hello message
82 # isn't part of the hello message
83 runcommand(server, ['id'])
83 runcommand(server, ['id'])
84
84
85 def checkruncommand(server):
85 def checkruncommand(server):
86 # hello block
86 # hello block
87 readchannel(server)
87 readchannel(server)
88
88
89 # no args
89 # no args
90 runcommand(server, [])
90 runcommand(server, [])
91
91
92 # global options
92 # global options
93 runcommand(server, ['id', '--quiet'])
93 runcommand(server, ['id', '--quiet'])
94
94
95 # make sure global options don't stick through requests
95 # make sure global options don't stick through requests
96 runcommand(server, ['id'])
96 runcommand(server, ['id'])
97
97
98 # --config
98 # --config
99 runcommand(server, ['id', '--config', 'ui.quiet=True'])
99 runcommand(server, ['id', '--config', 'ui.quiet=True'])
100
100
101 # make sure --config doesn't stick
101 # make sure --config doesn't stick
102 runcommand(server, ['id'])
102 runcommand(server, ['id'])
103
103
104 def inputeof(server):
104 def inputeof(server):
105 readchannel(server)
105 readchannel(server)
106 server.stdin.write('runcommand\n')
106 server.stdin.write('runcommand\n')
107 # close stdin while server is waiting for input
107 # close stdin while server is waiting for input
108 server.stdin.close()
108 server.stdin.close()
109
109
110 # server exits with 1 if the pipe closed while reading the command
110 # server exits with 1 if the pipe closed while reading the command
111 print 'server exit code =', server.wait()
111 print 'server exit code =', server.wait()
112
112
113 def serverinput(server):
113 def serverinput(server):
114 readchannel(server)
114 readchannel(server)
115
115
116 patch = """
116 patch = """
117 # HG changeset patch
117 # HG changeset patch
118 # User test
118 # User test
119 # Date 0 0
119 # Date 0 0
120 # Node ID c103a3dec114d882c98382d684d8af798d09d857
120 # Node ID c103a3dec114d882c98382d684d8af798d09d857
121 # Parent 0000000000000000000000000000000000000000
121 # Parent 0000000000000000000000000000000000000000
122 1
122 1
123
123
124 diff -r 000000000000 -r c103a3dec114 a
124 diff -r 000000000000 -r c103a3dec114 a
125 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
125 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
126 +++ b/a Thu Jan 01 00:00:00 1970 +0000
126 +++ b/a Thu Jan 01 00:00:00 1970 +0000
127 @@ -0,0 +1,1 @@
127 @@ -0,0 +1,1 @@
128 +1
128 +1
129 """
129 """
130
130
131 runcommand(server, ['import', '-'], input=cStringIO.StringIO(patch))
131 runcommand(server, ['import', '-'], input=cStringIO.StringIO(patch))
132 runcommand(server, ['log'])
132 runcommand(server, ['log'])
133
133
134 def cwd(server):
134 def cwd(server):
135 """ check that --cwd doesn't persist between requests """
135 """ check that --cwd doesn't persist between requests """
136 readchannel(server)
136 readchannel(server)
137 os.mkdir('foo')
137 os.mkdir('foo')
138 f = open('foo/bar', 'wb')
138 f = open('foo/bar', 'wb')
139 f.write('a')
139 f.write('a')
140 f.close()
140 f.close()
141 runcommand(server, ['--cwd', 'foo', 'st', 'bar'])
141 runcommand(server, ['--cwd', 'foo', 'st', 'bar'])
142 runcommand(server, ['st', 'foo/bar'])
142 runcommand(server, ['st', 'foo/bar'])
143 os.remove('foo/bar')
143 os.remove('foo/bar')
144
144
145 def localhgrc(server):
145 def localhgrc(server):
146 """ check that local configs for the cached repo aren't inherited when -R
146 """ check that local configs for the cached repo aren't inherited when -R
147 is used """
147 is used """
148 readchannel(server)
148 readchannel(server)
149
149
150 # the cached repo local hgrc contains ui.foo=bar, so showconfig should
150 # the cached repo local hgrc contains ui.foo=bar, so showconfig should
151 # show it
151 # show it
152 runcommand(server, ['showconfig'])
152 runcommand(server, ['showconfig'])
153
153
154 # but not for this repo
154 # but not for this repo
155 runcommand(server, ['init', 'foo'])
155 runcommand(server, ['init', 'foo'])
156 runcommand(server, ['-R', 'foo', 'showconfig', 'ui', 'defaults'])
156 runcommand(server, ['-R', 'foo', 'showconfig', 'ui', 'defaults'])
157 shutil.rmtree('foo')
157 shutil.rmtree('foo')
158
158
159 def hook(**args):
159 def hook(**args):
160 print 'hook talking'
160 print 'hook talking'
161 print 'now try to read something: %r' % sys.stdin.read()
161 print 'now try to read something: %r' % sys.stdin.read()
162
162
163 def hookoutput(server):
163 def hookoutput(server):
164 readchannel(server)
164 readchannel(server)
165 runcommand(server, ['--config',
165 runcommand(server, ['--config',
166 'hooks.pre-identify=python:test-commandserver.hook',
166 'hooks.pre-identify=python:test-commandserver.hook',
167 'id'],
167 'id'],
168 input=cStringIO.StringIO('some input'))
168 input=cStringIO.StringIO('some input'))
169
169
170 def outsidechanges(server):
170 def outsidechanges(server):
171 readchannel(server)
171 readchannel(server)
172 f = open('a', 'ab')
172 f = open('a', 'ab')
173 f.write('a\n')
173 f.write('a\n')
174 f.close()
174 f.close()
175 runcommand(server, ['status'])
175 runcommand(server, ['status'])
176 os.system('hg ci -Am2')
176 os.system('hg ci -Am2')
177 runcommand(server, ['tip'])
177 runcommand(server, ['tip'])
178 runcommand(server, ['status'])
178 runcommand(server, ['status'])
179
179
180 def bookmarks(server):
180 def bookmarks(server):
181 readchannel(server)
181 readchannel(server)
182 runcommand(server, ['bookmarks'])
182 runcommand(server, ['bookmarks'])
183
183
184 # changes .hg/bookmarks
184 # changes .hg/bookmarks
185 os.system('hg bookmark -i bm1')
185 os.system('hg bookmark -i bm1')
186 os.system('hg bookmark -i bm2')
186 os.system('hg bookmark -i bm2')
187 runcommand(server, ['bookmarks'])
187 runcommand(server, ['bookmarks'])
188
188
189 # changes .hg/bookmarks.current
189 # changes .hg/bookmarks.current
190 os.system('hg upd bm1 -q')
190 os.system('hg upd bm1 -q')
191 runcommand(server, ['bookmarks'])
191 runcommand(server, ['bookmarks'])
192
192
193 runcommand(server, ['bookmarks', 'bm3'])
193 runcommand(server, ['bookmarks', 'bm3'])
194 f = open('a', 'ab')
194 f = open('a', 'ab')
195 f.write('a\n')
195 f.write('a\n')
196 f.close()
196 f.close()
197 runcommand(server, ['commit', '-Amm'])
197 runcommand(server, ['commit', '-Amm'])
198 runcommand(server, ['bookmarks'])
198 runcommand(server, ['bookmarks'])
199
199
200 def tagscache(server):
200 def tagscache(server):
201 readchannel(server)
201 readchannel(server)
202 runcommand(server, ['id', '-t', '-r', '0'])
202 runcommand(server, ['id', '-t', '-r', '0'])
203 os.system('hg tag -r 0 foo')
203 os.system('hg tag -r 0 foo')
204 runcommand(server, ['id', '-t', '-r', '0'])
204 runcommand(server, ['id', '-t', '-r', '0'])
205
205
206 def setphase(server):
206 def setphase(server):
207 readchannel(server)
207 readchannel(server)
208 runcommand(server, ['phase', '-r', '.'])
208 runcommand(server, ['phase', '-r', '.'])
209 os.system('hg phase -r . -p')
209 os.system('hg phase -r . -p')
210 runcommand(server, ['phase', '-r', '.'])
210 runcommand(server, ['phase', '-r', '.'])
211
211
212 def rollback(server):
212 def rollback(server):
213 readchannel(server)
213 readchannel(server)
214 runcommand(server, ['phase', '-r', '.', '-p'])
214 runcommand(server, ['phase', '-r', '.', '-p'])
215 f = open('a', 'ab')
215 f = open('a', 'ab')
216 f.write('a\n')
216 f.write('a\n')
217 f.close()
217 f.close()
218 runcommand(server, ['commit', '-Am.'])
218 runcommand(server, ['commit', '-Am.'])
219 runcommand(server, ['rollback'])
219 runcommand(server, ['rollback'])
220 runcommand(server, ['phase', '-r', '.'])
220 runcommand(server, ['phase', '-r', '.'])
221
221
222 def branch(server):
222 def branch(server):
223 readchannel(server)
223 readchannel(server)
224 runcommand(server, ['branch'])
224 runcommand(server, ['branch'])
225 os.system('hg branch foo')
225 os.system('hg branch foo')
226 runcommand(server, ['branch'])
226 runcommand(server, ['branch'])
227 os.system('hg branch default')
227 os.system('hg branch default')
228
228
229 def hgignore(server):
229 def hgignore(server):
230 readchannel(server)
230 readchannel(server)
231 f = open('.hgignore', 'ab')
231 f = open('.hgignore', 'ab')
232 f.write('')
232 f.write('')
233 f.close()
233 f.close()
234 runcommand(server, ['commit', '-Am.'])
234 runcommand(server, ['commit', '-Am.'])
235 f = open('ignored-file', 'ab')
235 f = open('ignored-file', 'ab')
236 f.write('')
236 f.write('')
237 f.close()
237 f.close()
238 f = open('.hgignore', 'ab')
238 f = open('.hgignore', 'ab')
239 f.write('ignored-file')
239 f.write('ignored-file')
240 f.close()
240 f.close()
241 runcommand(server, ['status', '-i', '-u'])
241 runcommand(server, ['status', '-i', '-u'])
242
242
243 def phasecacheafterstrip(server):
243 def phasecacheafterstrip(server):
244 readchannel(server)
244 readchannel(server)
245
245
246 # create new head, 5:731265503d86
246 # create new head, 5:731265503d86
247 runcommand(server, ['update', '-C', '0'])
247 runcommand(server, ['update', '-C', '0'])
248 f = open('a', 'ab')
248 f = open('a', 'ab')
249 f.write('a\n')
249 f.write('a\n')
250 f.close()
250 f.close()
251 runcommand(server, ['commit', '-Am.', 'a'])
251 runcommand(server, ['commit', '-Am.', 'a'])
252 runcommand(server, ['log', '-Gq'])
252 runcommand(server, ['log', '-Gq'])
253
253
254 # make it public; draft marker moves to 4:7966c8e3734d
254 # make it public; draft marker moves to 4:7966c8e3734d
255 runcommand(server, ['phase', '-p', '.'])
255 runcommand(server, ['phase', '-p', '.'])
256 # load _phasecache.phaseroots
256 # load _phasecache.phaseroots
257 runcommand(server, ['phase', '.'], outfilter=sep)
257 runcommand(server, ['phase', '.'], outfilter=sep)
258
258
259 # strip 1::4 outside server
259 # strip 1::4 outside server
260 os.system('hg -q --config extensions.mq= strip 1')
260 os.system('hg -q --config extensions.mq= strip 1')
261
261
262 # shouldn't raise "7966c8e3734d: no node!"
262 # shouldn't raise "7966c8e3734d: no node!"
263 runcommand(server, ['branches'])
263 runcommand(server, ['branches'])
264
264
265 def obsolete(server):
265 def obsolete(server):
266 readchannel(server)
266 readchannel(server)
267
267
268 runcommand(server, ['up', 'null'])
268 runcommand(server, ['up', 'null'])
269 runcommand(server, ['phase', '-df', 'tip'])
269 runcommand(server, ['phase', '-df', 'tip'])
270 cmd = 'hg debugobsolete `hg log -r tip --template {node}`'
270 cmd = 'hg debugobsolete `hg log -r tip --template {node}`'
271 if os.name == 'nt':
271 if os.name == 'nt':
272 cmd = 'sh -c "%s"' % cmd # run in sh, not cmd.exe
272 cmd = 'sh -c "%s"' % cmd # run in sh, not cmd.exe
273 os.system(cmd)
273 os.system(cmd)
274 runcommand(server, ['log', '--hidden'])
274 runcommand(server, ['log', '--hidden'])
275 runcommand(server, ['log'])
275 runcommand(server, ['log'])
276
276
277 def mqoutsidechanges(server):
277 def mqoutsidechanges(server):
278 readchannel(server)
278 readchannel(server)
279
279
280 # load repo.mq
280 # load repo.mq
281 runcommand(server, ['qapplied'])
281 runcommand(server, ['qapplied'])
282 os.system('hg qnew 0.diff')
282 os.system('hg qnew 0.diff')
283 # repo.mq should be invalidated
283 # repo.mq should be invalidated
284 runcommand(server, ['qapplied'])
284 runcommand(server, ['qapplied'])
285
285
286 runcommand(server, ['qpop', '--all'])
287 os.system('hg qqueue --create foo')
288 # repo.mq should be recreated to point to new queue
289 runcommand(server, ['qqueue', '--active'])
290
286 if __name__ == '__main__':
291 if __name__ == '__main__':
287 os.system('hg init')
292 os.system('hg init')
288
293
289 check(hellomessage)
294 check(hellomessage)
290 check(unknowncommand)
295 check(unknowncommand)
291 check(checkruncommand)
296 check(checkruncommand)
292 check(inputeof)
297 check(inputeof)
293 check(serverinput)
298 check(serverinput)
294 check(cwd)
299 check(cwd)
295
300
296 hgrc = open('.hg/hgrc', 'a')
301 hgrc = open('.hg/hgrc', 'a')
297 hgrc.write('[ui]\nfoo=bar\n')
302 hgrc.write('[ui]\nfoo=bar\n')
298 hgrc.close()
303 hgrc.close()
299 check(localhgrc)
304 check(localhgrc)
300 check(hookoutput)
305 check(hookoutput)
301 check(outsidechanges)
306 check(outsidechanges)
302 check(bookmarks)
307 check(bookmarks)
303 check(tagscache)
308 check(tagscache)
304 check(setphase)
309 check(setphase)
305 check(rollback)
310 check(rollback)
306 check(branch)
311 check(branch)
307 check(hgignore)
312 check(hgignore)
308 check(phasecacheafterstrip)
313 check(phasecacheafterstrip)
309 obs = open('obs.py', 'w')
314 obs = open('obs.py', 'w')
310 obs.write('import mercurial.obsolete\nmercurial.obsolete._enabled = True\n')
315 obs.write('import mercurial.obsolete\nmercurial.obsolete._enabled = True\n')
311 obs.close()
316 obs.close()
312 hgrc = open('.hg/hgrc', 'a')
317 hgrc = open('.hg/hgrc', 'a')
313 hgrc.write('[extensions]\nobs=obs.py\n')
318 hgrc.write('[extensions]\nobs=obs.py\n')
314 hgrc.close()
319 hgrc.close()
315 check(obsolete)
320 check(obsolete)
316 hgrc = open('.hg/hgrc', 'a')
321 hgrc = open('.hg/hgrc', 'a')
317 hgrc.write('[extensions]\nmq=\n')
322 hgrc.write('[extensions]\nmq=\n')
318 hgrc.close()
323 hgrc.close()
319 check(mqoutsidechanges)
324 check(mqoutsidechanges)
@@ -1,230 +1,235 b''
1
1
2 testing hellomessage:
2 testing hellomessage:
3
3
4 o, 'capabilities: getencoding runcommand\nencoding: ***'
4 o, 'capabilities: getencoding runcommand\nencoding: ***'
5 runcommand id
5 runcommand id
6 000000000000 tip
6 000000000000 tip
7
7
8 testing unknowncommand:
8 testing unknowncommand:
9
9
10 abort: unknown command unknowncommand
10 abort: unknown command unknowncommand
11
11
12 testing checkruncommand:
12 testing checkruncommand:
13
13
14 runcommand
14 runcommand
15 Mercurial Distributed SCM
15 Mercurial Distributed SCM
16
16
17 basic commands:
17 basic commands:
18
18
19 add add the specified files on the next commit
19 add add the specified files on the next commit
20 annotate show changeset information by line for each file
20 annotate show changeset information by line for each file
21 clone make a copy of an existing repository
21 clone make a copy of an existing repository
22 commit commit the specified files or all outstanding changes
22 commit commit the specified files or all outstanding changes
23 diff diff repository (or selected files)
23 diff diff repository (or selected files)
24 export dump the header and diffs for one or more changesets
24 export dump the header and diffs for one or more changesets
25 forget forget the specified files on the next commit
25 forget forget the specified files on the next commit
26 init create a new repository in the given directory
26 init create a new repository in the given directory
27 log show revision history of entire repository or files
27 log show revision history of entire repository or files
28 merge merge working directory with another revision
28 merge merge working directory with another revision
29 pull pull changes from the specified source
29 pull pull changes from the specified source
30 push push changes to the specified destination
30 push push changes to the specified destination
31 remove remove the specified files on the next commit
31 remove remove the specified files on the next commit
32 serve start stand-alone webserver
32 serve start stand-alone webserver
33 status show changed files in the working directory
33 status show changed files in the working directory
34 summary summarize working directory state
34 summary summarize working directory state
35 update update working directory (or switch revisions)
35 update update working directory (or switch revisions)
36
36
37 use "hg help" for the full list of commands or "hg -v" for details
37 use "hg help" for the full list of commands or "hg -v" for details
38 runcommand id --quiet
38 runcommand id --quiet
39 000000000000
39 000000000000
40 runcommand id
40 runcommand id
41 000000000000 tip
41 000000000000 tip
42 runcommand id --config ui.quiet=True
42 runcommand id --config ui.quiet=True
43 000000000000
43 000000000000
44 runcommand id
44 runcommand id
45 000000000000 tip
45 000000000000 tip
46
46
47 testing inputeof:
47 testing inputeof:
48
48
49 server exit code = 1
49 server exit code = 1
50
50
51 testing serverinput:
51 testing serverinput:
52
52
53 runcommand import -
53 runcommand import -
54 applying patch from stdin
54 applying patch from stdin
55 runcommand log
55 runcommand log
56 changeset: 0:eff892de26ec
56 changeset: 0:eff892de26ec
57 tag: tip
57 tag: tip
58 user: test
58 user: test
59 date: Thu Jan 01 00:00:00 1970 +0000
59 date: Thu Jan 01 00:00:00 1970 +0000
60 summary: 1
60 summary: 1
61
61
62
62
63 testing cwd:
63 testing cwd:
64
64
65 runcommand --cwd foo st bar
65 runcommand --cwd foo st bar
66 ? bar
66 ? bar
67 runcommand st foo/bar
67 runcommand st foo/bar
68 ? foo/bar
68 ? foo/bar
69
69
70 testing localhgrc:
70 testing localhgrc:
71
71
72 runcommand showconfig
72 runcommand showconfig
73 bundle.mainreporoot=$TESTTMP
73 bundle.mainreporoot=$TESTTMP
74 defaults.backout=-d "0 0"
74 defaults.backout=-d "0 0"
75 defaults.commit=-d "0 0"
75 defaults.commit=-d "0 0"
76 defaults.shelve=--date "0 0"
76 defaults.shelve=--date "0 0"
77 defaults.tag=-d "0 0"
77 defaults.tag=-d "0 0"
78 ui.slash=True
78 ui.slash=True
79 ui.interactive=False
79 ui.interactive=False
80 ui.foo=bar
80 ui.foo=bar
81 runcommand init foo
81 runcommand init foo
82 runcommand -R foo showconfig ui defaults
82 runcommand -R foo showconfig ui defaults
83 defaults.backout=-d "0 0"
83 defaults.backout=-d "0 0"
84 defaults.commit=-d "0 0"
84 defaults.commit=-d "0 0"
85 defaults.shelve=--date "0 0"
85 defaults.shelve=--date "0 0"
86 defaults.tag=-d "0 0"
86 defaults.tag=-d "0 0"
87 ui.slash=True
87 ui.slash=True
88 ui.interactive=False
88 ui.interactive=False
89
89
90 testing hookoutput:
90 testing hookoutput:
91
91
92 runcommand --config hooks.pre-identify=python:test-commandserver.hook id
92 runcommand --config hooks.pre-identify=python:test-commandserver.hook id
93 hook talking
93 hook talking
94 now try to read something: 'some input'
94 now try to read something: 'some input'
95 eff892de26ec tip
95 eff892de26ec tip
96
96
97 testing outsidechanges:
97 testing outsidechanges:
98
98
99 runcommand status
99 runcommand status
100 M a
100 M a
101 runcommand tip
101 runcommand tip
102 changeset: 1:d3a0a68be6de
102 changeset: 1:d3a0a68be6de
103 tag: tip
103 tag: tip
104 user: test
104 user: test
105 date: Thu Jan 01 00:00:00 1970 +0000
105 date: Thu Jan 01 00:00:00 1970 +0000
106 summary: 2
106 summary: 2
107
107
108 runcommand status
108 runcommand status
109
109
110 testing bookmarks:
110 testing bookmarks:
111
111
112 runcommand bookmarks
112 runcommand bookmarks
113 no bookmarks set
113 no bookmarks set
114 runcommand bookmarks
114 runcommand bookmarks
115 bm1 1:d3a0a68be6de
115 bm1 1:d3a0a68be6de
116 bm2 1:d3a0a68be6de
116 bm2 1:d3a0a68be6de
117 runcommand bookmarks
117 runcommand bookmarks
118 * bm1 1:d3a0a68be6de
118 * bm1 1:d3a0a68be6de
119 bm2 1:d3a0a68be6de
119 bm2 1:d3a0a68be6de
120 runcommand bookmarks bm3
120 runcommand bookmarks bm3
121 runcommand commit -Amm
121 runcommand commit -Amm
122 runcommand bookmarks
122 runcommand bookmarks
123 bm1 1:d3a0a68be6de
123 bm1 1:d3a0a68be6de
124 bm2 1:d3a0a68be6de
124 bm2 1:d3a0a68be6de
125 * bm3 2:aef17e88f5f0
125 * bm3 2:aef17e88f5f0
126
126
127 testing tagscache:
127 testing tagscache:
128
128
129 runcommand id -t -r 0
129 runcommand id -t -r 0
130
130
131 runcommand id -t -r 0
131 runcommand id -t -r 0
132 foo
132 foo
133
133
134 testing setphase:
134 testing setphase:
135
135
136 runcommand phase -r .
136 runcommand phase -r .
137 3: draft
137 3: draft
138 runcommand phase -r .
138 runcommand phase -r .
139 3: public
139 3: public
140
140
141 testing rollback:
141 testing rollback:
142
142
143 runcommand phase -r . -p
143 runcommand phase -r . -p
144 no phases changed
144 no phases changed
145 runcommand commit -Am.
145 runcommand commit -Am.
146 runcommand rollback
146 runcommand rollback
147 repository tip rolled back to revision 3 (undo commit)
147 repository tip rolled back to revision 3 (undo commit)
148 working directory now based on revision 3
148 working directory now based on revision 3
149 runcommand phase -r .
149 runcommand phase -r .
150 3: public
150 3: public
151
151
152 testing branch:
152 testing branch:
153
153
154 runcommand branch
154 runcommand branch
155 default
155 default
156 marked working directory as branch foo
156 marked working directory as branch foo
157 (branches are permanent and global, did you want a bookmark?)
157 (branches are permanent and global, did you want a bookmark?)
158 runcommand branch
158 runcommand branch
159 foo
159 foo
160 marked working directory as branch default
160 marked working directory as branch default
161 (branches are permanent and global, did you want a bookmark?)
161 (branches are permanent and global, did you want a bookmark?)
162
162
163 testing hgignore:
163 testing hgignore:
164
164
165 runcommand commit -Am.
165 runcommand commit -Am.
166 adding .hgignore
166 adding .hgignore
167 runcommand status -i -u
167 runcommand status -i -u
168 I ignored-file
168 I ignored-file
169
169
170 testing phasecacheafterstrip:
170 testing phasecacheafterstrip:
171
171
172 runcommand update -C 0
172 runcommand update -C 0
173 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
173 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
174 runcommand commit -Am. a
174 runcommand commit -Am. a
175 created new head
175 created new head
176 runcommand log -Gq
176 runcommand log -Gq
177 @ 5:731265503d86
177 @ 5:731265503d86
178 |
178 |
179 | o 4:7966c8e3734d
179 | o 4:7966c8e3734d
180 | |
180 | |
181 | o 3:b9b85890c400
181 | o 3:b9b85890c400
182 | |
182 | |
183 | o 2:aef17e88f5f0
183 | o 2:aef17e88f5f0
184 | |
184 | |
185 | o 1:d3a0a68be6de
185 | o 1:d3a0a68be6de
186 |/
186 |/
187 o 0:eff892de26ec
187 o 0:eff892de26ec
188
188
189 runcommand phase -p .
189 runcommand phase -p .
190 runcommand phase .
190 runcommand phase .
191 5: public
191 5: public
192 runcommand branches
192 runcommand branches
193 default 1:731265503d86
193 default 1:731265503d86
194
194
195 testing obsolete:
195 testing obsolete:
196
196
197 runcommand up null
197 runcommand up null
198 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
198 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
199 runcommand phase -df tip
199 runcommand phase -df tip
200 runcommand log --hidden
200 runcommand log --hidden
201 changeset: 1:731265503d86
201 changeset: 1:731265503d86
202 tag: tip
202 tag: tip
203 user: test
203 user: test
204 date: Thu Jan 01 00:00:00 1970 +0000
204 date: Thu Jan 01 00:00:00 1970 +0000
205 summary: .
205 summary: .
206
206
207 changeset: 0:eff892de26ec
207 changeset: 0:eff892de26ec
208 bookmark: bm1
208 bookmark: bm1
209 bookmark: bm2
209 bookmark: bm2
210 bookmark: bm3
210 bookmark: bm3
211 user: test
211 user: test
212 date: Thu Jan 01 00:00:00 1970 +0000
212 date: Thu Jan 01 00:00:00 1970 +0000
213 summary: 1
213 summary: 1
214
214
215 runcommand log
215 runcommand log
216 changeset: 0:eff892de26ec
216 changeset: 0:eff892de26ec
217 bookmark: bm1
217 bookmark: bm1
218 bookmark: bm2
218 bookmark: bm2
219 bookmark: bm3
219 bookmark: bm3
220 tag: tip
220 tag: tip
221 user: test
221 user: test
222 date: Thu Jan 01 00:00:00 1970 +0000
222 date: Thu Jan 01 00:00:00 1970 +0000
223 summary: 1
223 summary: 1
224
224
225
225
226 testing mqoutsidechanges:
226 testing mqoutsidechanges:
227
227
228 runcommand qapplied
228 runcommand qapplied
229 runcommand qapplied
229 runcommand qapplied
230 0.diff
230 0.diff
231 runcommand qpop --all
232 popping 0.diff
233 patch queue now empty
234 runcommand qqueue --active
235 foo
General Comments 0
You need to be logged in to leave comments. Login now