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