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