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