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