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