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