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