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