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