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