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