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