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