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