Mercurial > hg
comparison hgext/mq.py @ 5148:06154aff2b1a
merge with -stable
author | Benoit Boissinot <benoit.boissinot@ens-lyon.org> |
---|---|
date | Wed, 08 Aug 2007 23:00:01 +0200 |
parents | fc502517d68d c80af96943aa |
children | f6c520fd70cf |
comparison
equal
deleted
inserted
replaced
5083:f94dbc6c7eaf | 5148:06154aff2b1a |
---|---|
321 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts) | 321 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts) |
322 | 322 |
323 patch.diff(repo, node1, node2, fns, match=matchfn, | 323 patch.diff(repo, node1, node2, fns, match=matchfn, |
324 fp=fp, changes=changes, opts=self.diffopts()) | 324 fp=fp, changes=changes, opts=self.diffopts()) |
325 | 325 |
326 def mergeone(self, repo, mergeq, head, patch, rev, wlock): | 326 def mergeone(self, repo, mergeq, head, patch, rev): |
327 # first try just applying the patch | 327 # first try just applying the patch |
328 (err, n) = self.apply(repo, [ patch ], update_status=False, | 328 (err, n) = self.apply(repo, [ patch ], update_status=False, |
329 strict=True, merge=rev, wlock=wlock) | 329 strict=True, merge=rev) |
330 | 330 |
331 if err == 0: | 331 if err == 0: |
332 return (err, n) | 332 return (err, n) |
333 | 333 |
334 if n is None: | 334 if n is None: |
335 raise util.Abort(_("apply failed for patch %s") % patch) | 335 raise util.Abort(_("apply failed for patch %s") % patch) |
336 | 336 |
337 self.ui.warn("patch didn't work out, merging %s\n" % patch) | 337 self.ui.warn("patch didn't work out, merging %s\n" % patch) |
338 | 338 |
339 # apply failed, strip away that rev and merge. | 339 # apply failed, strip away that rev and merge. |
340 hg.clean(repo, head, wlock=wlock) | 340 hg.clean(repo, head) |
341 self.strip(repo, n, update=False, backup='strip', wlock=wlock) | 341 self.strip(repo, n, update=False, backup='strip') |
342 | 342 |
343 ctx = repo.changectx(rev) | 343 ctx = repo.changectx(rev) |
344 ret = hg.merge(repo, rev, wlock=wlock) | 344 ret = hg.merge(repo, rev) |
345 if ret: | 345 if ret: |
346 raise util.Abort(_("update returned %d") % ret) | 346 raise util.Abort(_("update returned %d") % ret) |
347 n = repo.commit(None, ctx.description(), ctx.user(), | 347 n = repo.commit(None, ctx.description(), ctx.user(), force=1) |
348 force=1, wlock=wlock) | |
349 if n == None: | 348 if n == None: |
350 raise util.Abort(_("repo commit failed")) | 349 raise util.Abort(_("repo commit failed")) |
351 try: | 350 try: |
352 message, comments, user, date, patchfound = mergeq.readheaders(patch) | 351 message, comments, user, date, patchfound = mergeq.readheaders(patch) |
353 except: | 352 except: |
379 return pp[0] | 378 return pp[0] |
380 if p1 in arevs: | 379 if p1 in arevs: |
381 return pp[1] | 380 return pp[1] |
382 return pp[0] | 381 return pp[0] |
383 | 382 |
384 def mergepatch(self, repo, mergeq, series, wlock): | 383 def mergepatch(self, repo, mergeq, series): |
385 if len(self.applied) == 0: | 384 if len(self.applied) == 0: |
386 # each of the patches merged in will have two parents. This | 385 # each of the patches merged in will have two parents. This |
387 # can confuse the qrefresh, qdiff, and strip code because it | 386 # can confuse the qrefresh, qdiff, and strip code because it |
388 # needs to know which parent is actually in the patch queue. | 387 # needs to know which parent is actually in the patch queue. |
389 # so, we insert a merge marker with only one parent. This way | 388 # so, we insert a merge marker with only one parent. This way |
390 # the first patch in the queue is never a merge patch | 389 # the first patch in the queue is never a merge patch |
391 # | 390 # |
392 pname = ".hg.patches.merge.marker" | 391 pname = ".hg.patches.merge.marker" |
393 n = repo.commit(None, '[mq]: merge marker', user=None, force=1, | 392 n = repo.commit(None, '[mq]: merge marker', user=None, force=1) |
394 wlock=wlock) | |
395 self.removeundo(repo) | 393 self.removeundo(repo) |
396 self.applied.append(statusentry(revlog.hex(n), pname)) | 394 self.applied.append(statusentry(revlog.hex(n), pname)) |
397 self.applied_dirty = 1 | 395 self.applied_dirty = 1 |
398 | 396 |
399 head = self.qparents(repo) | 397 head = self.qparents(repo) |
410 info = mergeq.isapplied(patch) | 408 info = mergeq.isapplied(patch) |
411 if not info: | 409 if not info: |
412 self.ui.warn("patch %s is not applied\n" % patch) | 410 self.ui.warn("patch %s is not applied\n" % patch) |
413 return (1, None) | 411 return (1, None) |
414 rev = revlog.bin(info[1]) | 412 rev = revlog.bin(info[1]) |
415 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock) | 413 (err, head) = self.mergeone(repo, mergeq, head, patch, rev) |
416 if head: | 414 if head: |
417 self.applied.append(statusentry(revlog.hex(head), patch)) | 415 self.applied.append(statusentry(revlog.hex(head), patch)) |
418 self.applied_dirty = 1 | 416 self.applied_dirty = 1 |
419 if err: | 417 if err: |
420 return (err, head) | 418 return (err, head) |
435 return (False, files, False) | 433 return (False, files, False) |
436 | 434 |
437 return (True, files, fuzz) | 435 return (True, files, fuzz) |
438 | 436 |
439 def apply(self, repo, series, list=False, update_status=True, | 437 def apply(self, repo, series, list=False, update_status=True, |
440 strict=False, patchdir=None, merge=None, wlock=None, | 438 strict=False, patchdir=None, merge=None, all_files={}): |
441 all_files={}): | 439 wlock = lock = tr = None |
442 if not wlock: | 440 try: |
443 wlock = repo.wlock() | 441 wlock = repo.wlock() |
444 lock = repo.lock() | 442 lock = repo.lock() |
445 tr = repo.transaction() | 443 tr = repo.transaction() |
446 try: | |
447 ret = self._apply(tr, repo, series, list, update_status, | |
448 strict, patchdir, merge, wlock, | |
449 lock=lock, all_files=all_files) | |
450 tr.close() | |
451 self.save_dirty() | |
452 return ret | |
453 except: | |
454 try: | 444 try: |
455 tr.abort() | 445 ret = self._apply(repo, series, list, update_status, |
456 finally: | 446 strict, patchdir, merge, all_files=all_files) |
457 repo.invalidate() | 447 tr.close() |
458 repo.dirstate.invalidate() | 448 self.save_dirty() |
459 raise | 449 return ret |
460 | 450 except: |
461 def _apply(self, tr, repo, series, list=False, update_status=True, | 451 try: |
462 strict=False, patchdir=None, merge=None, wlock=None, | 452 tr.abort() |
463 lock=None, all_files={}): | 453 finally: |
454 repo.invalidate() | |
455 repo.dirstate.invalidate() | |
456 raise | |
457 finally: | |
458 del tr, lock, wlock | |
459 | |
460 def _apply(self, repo, series, list=False, update_status=True, | |
461 strict=False, patchdir=None, merge=None, all_files={}): | |
464 # TODO unify with commands.py | 462 # TODO unify with commands.py |
465 if not patchdir: | 463 if not patchdir: |
466 patchdir = self.path | 464 patchdir = self.path |
467 err = 0 | 465 err = 0 |
468 n = None | 466 n = None |
495 if merge and files: | 493 if merge and files: |
496 # Mark as removed/merged and update dirstate parent info | 494 # Mark as removed/merged and update dirstate parent info |
497 removed = [] | 495 removed = [] |
498 merged = [] | 496 merged = [] |
499 for f in files: | 497 for f in files: |
500 if os.path.exists(repo.dirstate.wjoin(f)): | 498 if os.path.exists(repo.wjoin(f)): |
501 merged.append(f) | 499 merged.append(f) |
502 else: | 500 else: |
503 removed.append(f) | 501 removed.append(f) |
504 repo.dirstate.update(repo.dirstate.filterfiles(removed), 'r') | 502 for f in removed: |
505 repo.dirstate.update(repo.dirstate.filterfiles(merged), 'm') | 503 repo.dirstate.remove(f) |
504 for f in merged: | |
505 repo.dirstate.merge(f) | |
506 p1, p2 = repo.dirstate.parents() | 506 p1, p2 = repo.dirstate.parents() |
507 repo.dirstate.setparents(p1, merge) | 507 repo.dirstate.setparents(p1, merge) |
508 files = patch.updatedir(self.ui, repo, files, wlock=wlock) | 508 files = patch.updatedir(self.ui, repo, files) |
509 n = repo.commit(files, message, user, date, force=1, lock=lock, | 509 n = repo.commit(files, message, user, date, force=1) |
510 wlock=wlock) | |
511 | 510 |
512 if n == None: | 511 if n == None: |
513 raise util.Abort(_("repo commit failed")) | 512 raise util.Abort(_("repo commit failed")) |
514 | 513 |
515 if update_status: | 514 if update_status: |
612 else: | 611 else: |
613 m, a, r, d = self.check_localchanges(repo, force) | 612 m, a, r, d = self.check_localchanges(repo, force) |
614 commitfiles = m + a + r | 613 commitfiles = m + a + r |
615 self.check_toppatch(repo) | 614 self.check_toppatch(repo) |
616 wlock = repo.wlock() | 615 wlock = repo.wlock() |
617 insert = self.full_series_end() | 616 try: |
618 if msg: | 617 insert = self.full_series_end() |
619 n = repo.commit(commitfiles, msg, force=True, wlock=wlock) | 618 if msg: |
620 else: | 619 n = repo.commit(commitfiles, msg, force=True) |
621 n = repo.commit(commitfiles, | 620 else: |
622 "[mq]: %s" % patch, force=True, wlock=wlock) | 621 n = repo.commit(commitfiles, "[mq]: %s" % patch, force=True) |
623 if n == None: | 622 if n == None: |
624 raise util.Abort(_("repo commit failed")) | 623 raise util.Abort(_("repo commit failed")) |
625 self.full_series[insert:insert] = [patch] | 624 self.full_series[insert:insert] = [patch] |
626 self.applied.append(statusentry(revlog.hex(n), patch)) | 625 self.applied.append(statusentry(revlog.hex(n), patch)) |
627 self.parse_series() | 626 self.parse_series() |
628 self.series_dirty = 1 | 627 self.series_dirty = 1 |
629 self.applied_dirty = 1 | 628 self.applied_dirty = 1 |
630 p = self.opener(patch, "w") | 629 p = self.opener(patch, "w") |
631 if msg: | 630 if msg: |
632 msg = msg + "\n" | 631 msg = msg + "\n" |
633 p.write(msg) | 632 p.write(msg) |
634 p.close() | 633 p.close() |
635 wlock = None | 634 wlock = None |
636 r = self.qrepo() | 635 r = self.qrepo() |
637 if r: r.add([patch]) | 636 if r: r.add([patch]) |
638 if commitfiles: | 637 if commitfiles: |
639 self.refresh(repo, short=True) | 638 self.refresh(repo, short=True, git=opts.get('git')) |
640 self.removeundo(repo) | 639 self.removeundo(repo) |
641 | 640 finally: |
642 def strip(self, repo, rev, update=True, backup="all", wlock=None): | 641 del wlock |
643 if not wlock: | 642 |
643 def strip(self, repo, rev, update=True, backup="all"): | |
644 wlock = lock = None | |
645 try: | |
644 wlock = repo.wlock() | 646 wlock = repo.wlock() |
645 lock = repo.lock() | 647 lock = repo.lock() |
646 | 648 |
647 if update: | 649 if update: |
648 self.check_localchanges(repo, refresh=False) | 650 self.check_localchanges(repo, refresh=False) |
649 urev = self.qparents(repo, rev) | 651 urev = self.qparents(repo, rev) |
650 hg.clean(repo, urev, wlock=wlock) | 652 hg.clean(repo, urev) |
651 repo.dirstate.write() | 653 repo.dirstate.write() |
652 | 654 |
653 self.removeundo(repo) | 655 self.removeundo(repo) |
654 repair.strip(self.ui, repo, rev, backup) | 656 repair.strip(self.ui, repo, rev, backup) |
657 finally: | |
658 del lock, wlock | |
655 | 659 |
656 def isapplied(self, patch): | 660 def isapplied(self, patch): |
657 """returns (index, rev, patch)""" | 661 """returns (index, rev, patch)""" |
658 for i in xrange(len(self.applied)): | 662 for i in xrange(len(self.applied)): |
659 a = self.applied[i] | 663 a = self.applied[i] |
733 if i + off < len(self.series): | 737 if i + off < len(self.series): |
734 return self.series[i + off] | 738 return self.series[i + off] |
735 raise util.Abort(_("patch %s not in series") % patch) | 739 raise util.Abort(_("patch %s not in series") % patch) |
736 | 740 |
737 def push(self, repo, patch=None, force=False, list=False, | 741 def push(self, repo, patch=None, force=False, list=False, |
738 mergeq=None, wlock=None): | 742 mergeq=None): |
739 if not wlock: | 743 wlock = repo.wlock() |
740 wlock = repo.wlock() | 744 try: |
741 patch = self.lookup(patch) | 745 patch = self.lookup(patch) |
742 # Suppose our series file is: A B C and the current 'top' patch is B. | 746 # Suppose our series file is: A B C and the current 'top' |
743 # qpush C should be performed (moving forward) | 747 # patch is B. qpush C should be performed (moving forward) |
744 # qpush B is a NOP (no change) | 748 # qpush B is a NOP (no change) qpush A is an error (can't |
745 # qpush A is an error (can't go backwards with qpush) | 749 # go backwards with qpush) |
746 if patch: | 750 if patch: |
747 info = self.isapplied(patch) | 751 info = self.isapplied(patch) |
748 if info: | 752 if info: |
749 if info[0] < len(self.applied) - 1: | 753 if info[0] < len(self.applied) - 1: |
750 raise util.Abort(_("cannot push to a previous patch: %s") % | 754 raise util.Abort( |
751 patch) | 755 _("cannot push to a previous patch: %s") % patch) |
752 if info[0] < len(self.series) - 1: | 756 if info[0] < len(self.series) - 1: |
753 self.ui.warn(_('qpush: %s is already at the top\n') % patch) | 757 self.ui.warn( |
758 _('qpush: %s is already at the top\n') % patch) | |
759 else: | |
760 self.ui.warn(_('all patches are currently applied\n')) | |
761 return | |
762 | |
763 # Following the above example, starting at 'top' of B: | |
764 # qpush should be performed (pushes C), but a subsequent | |
765 # qpush without an argument is an error (nothing to | |
766 # apply). This allows a loop of "...while hg qpush..." to | |
767 # work as it detects an error when done | |
768 if self.series_end() == len(self.series): | |
769 self.ui.warn(_('patch series already fully applied\n')) | |
770 return 1 | |
771 if not force: | |
772 self.check_localchanges(repo) | |
773 | |
774 self.applied_dirty = 1; | |
775 start = self.series_end() | |
776 if start > 0: | |
777 self.check_toppatch(repo) | |
778 if not patch: | |
779 patch = self.series[start] | |
780 end = start + 1 | |
781 else: | |
782 end = self.series.index(patch, start) + 1 | |
783 s = self.series[start:end] | |
784 all_files = {} | |
785 try: | |
786 if mergeq: | |
787 ret = self.mergepatch(repo, mergeq, s) | |
754 else: | 788 else: |
755 self.ui.warn(_('all patches are currently applied\n')) | 789 ret = self.apply(repo, s, list, all_files=all_files) |
756 return | 790 except: |
757 | 791 self.ui.warn(_('cleaning up working directory...')) |
758 # Following the above example, starting at 'top' of B: | 792 node = repo.dirstate.parents()[0] |
759 # qpush should be performed (pushes C), but a subsequent qpush without | 793 hg.revert(repo, node, None) |
760 # an argument is an error (nothing to apply). This allows a loop | 794 unknown = repo.status()[4] |
761 # of "...while hg qpush..." to work as it detects an error when done | 795 # only remove unknown files that we know we touched or |
762 if self.series_end() == len(self.series): | 796 # created while patching |
763 self.ui.warn(_('patch series already fully applied\n')) | 797 for f in unknown: |
764 return 1 | 798 if f in all_files: |
765 if not force: | 799 util.unlink(repo.wjoin(f)) |
766 self.check_localchanges(repo) | 800 self.ui.warn(_('done\n')) |
767 | 801 raise |
768 self.applied_dirty = 1; | 802 top = self.applied[-1].name |
769 start = self.series_end() | 803 if ret[0]: |
770 if start > 0: | 804 self.ui.write( |
771 self.check_toppatch(repo) | 805 "Errors during apply, please fix and refresh %s\n" % top) |
772 if not patch: | |
773 patch = self.series[start] | |
774 end = start + 1 | |
775 else: | |
776 end = self.series.index(patch, start) + 1 | |
777 s = self.series[start:end] | |
778 all_files = {} | |
779 try: | |
780 if mergeq: | |
781 ret = self.mergepatch(repo, mergeq, s, wlock) | |
782 else: | 806 else: |
783 ret = self.apply(repo, s, list, wlock=wlock, | 807 self.ui.write("Now at: %s\n" % top) |
784 all_files=all_files) | 808 return ret[0] |
785 except: | 809 finally: |
786 self.ui.warn(_('cleaning up working directory...')) | 810 del wlock |
787 node = repo.dirstate.parents()[0] | 811 |
788 hg.revert(repo, node, None, wlock) | 812 def pop(self, repo, patch=None, force=False, update=True, all=False): |
789 unknown = repo.status(wlock=wlock)[4] | |
790 # only remove unknown files that we know we touched or | |
791 # created while patching | |
792 for f in unknown: | |
793 if f in all_files: | |
794 util.unlink(repo.wjoin(f)) | |
795 self.ui.warn(_('done\n')) | |
796 raise | |
797 top = self.applied[-1].name | |
798 if ret[0]: | |
799 self.ui.write("Errors during apply, please fix and refresh %s\n" % | |
800 top) | |
801 else: | |
802 self.ui.write("Now at: %s\n" % top) | |
803 return ret[0] | |
804 | |
805 def pop(self, repo, patch=None, force=False, update=True, all=False, | |
806 wlock=None): | |
807 def getfile(f, rev): | 813 def getfile(f, rev): |
808 t = repo.file(f).read(rev) | 814 t = repo.file(f).read(rev) |
809 repo.wfile(f, "w").write(t) | 815 repo.wfile(f, "w").write(t) |
810 | 816 |
811 if not wlock: | 817 wlock = repo.wlock() |
812 wlock = repo.wlock() | 818 try: |
813 if patch: | 819 if patch: |
814 # index, rev, patch | 820 # index, rev, patch |
815 info = self.isapplied(patch) | 821 info = self.isapplied(patch) |
816 if not info: | 822 if not info: |
817 patch = self.lookup(patch) | 823 patch = self.lookup(patch) |
818 info = self.isapplied(patch) | 824 info = self.isapplied(patch) |
819 if not info: | 825 if not info: |
820 raise util.Abort(_("patch %s is not applied") % patch) | 826 raise util.Abort(_("patch %s is not applied") % patch) |
821 | 827 |
822 if len(self.applied) == 0: | 828 if len(self.applied) == 0: |
823 # Allow qpop -a to work repeatedly, | 829 # Allow qpop -a to work repeatedly, |
824 # but not qpop without an argument | 830 # but not qpop without an argument |
825 self.ui.warn(_("no patches applied\n")) | 831 self.ui.warn(_("no patches applied\n")) |
826 return not all | 832 return not all |
827 | 833 |
828 if not update: | 834 if not update: |
829 parents = repo.dirstate.parents() | 835 parents = repo.dirstate.parents() |
830 rr = [ revlog.bin(x.rev) for x in self.applied ] | 836 rr = [ revlog.bin(x.rev) for x in self.applied ] |
831 for p in parents: | 837 for p in parents: |
832 if p in rr: | 838 if p in rr: |
833 self.ui.warn("qpop: forcing dirstate update\n") | 839 self.ui.warn("qpop: forcing dirstate update\n") |
834 update = True | 840 update = True |
835 | 841 |
836 if not force and update: | 842 if not force and update: |
837 self.check_localchanges(repo) | 843 self.check_localchanges(repo) |
838 | 844 |
839 self.applied_dirty = 1; | 845 self.applied_dirty = 1; |
840 end = len(self.applied) | 846 end = len(self.applied) |
841 if not patch: | 847 if not patch: |
842 if all: | 848 if all: |
843 popi = 0 | 849 popi = 0 |
850 else: | |
851 popi = len(self.applied) - 1 | |
844 else: | 852 else: |
845 popi = len(self.applied) - 1 | 853 popi = info[0] + 1 |
846 else: | 854 if popi >= end: |
847 popi = info[0] + 1 | 855 self.ui.warn("qpop: %s is already at the top\n" % patch) |
848 if popi >= end: | 856 return |
849 self.ui.warn("qpop: %s is already at the top\n" % patch) | 857 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name] |
850 return | 858 |
851 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name] | 859 start = info[0] |
852 | 860 rev = revlog.bin(info[1]) |
853 start = info[0] | 861 |
854 rev = revlog.bin(info[1]) | 862 # we know there are no local changes, so we can make a simplified |
855 | 863 # form of hg.update. |
856 # we know there are no local changes, so we can make a simplified | 864 if update: |
857 # form of hg.update. | 865 top = self.check_toppatch(repo) |
858 if update: | 866 qp = self.qparents(repo, rev) |
859 top = self.check_toppatch(repo) | 867 changes = repo.changelog.read(qp) |
860 qp = self.qparents(repo, rev) | 868 mmap = repo.manifest.read(changes[0]) |
861 changes = repo.changelog.read(qp) | 869 m, a, r, d, u = repo.status(qp, top)[:5] |
862 mmap = repo.manifest.read(changes[0]) | 870 if d: |
863 m, a, r, d, u = repo.status(qp, top)[:5] | 871 raise util.Abort("deletions found between repo revs") |
864 if d: | 872 for f in m: |
865 raise util.Abort("deletions found between repo revs") | 873 getfile(f, mmap[f]) |
866 for f in m: | 874 for f in r: |
867 getfile(f, mmap[f]) | 875 getfile(f, mmap[f]) |
868 for f in r: | 876 util.set_exec(repo.wjoin(f), mmap.execf(f)) |
869 getfile(f, mmap[f]) | 877 for f in m + r: |
870 util.set_exec(repo.wjoin(f), mmap.execf(f)) | 878 repo.dirstate.normal(f) |
871 repo.dirstate.update(m + r, 'n') | 879 for f in a: |
872 for f in a: | 880 try: |
873 try: | 881 os.unlink(repo.wjoin(f)) |
874 os.unlink(repo.wjoin(f)) | 882 except OSError, e: |
875 except OSError, e: | 883 if e.errno != errno.ENOENT: |
876 if e.errno != errno.ENOENT: | 884 raise |
877 raise | 885 try: os.removedirs(os.path.dirname(repo.wjoin(f))) |
878 try: os.removedirs(os.path.dirname(repo.wjoin(f))) | 886 except: pass |
879 except: pass | 887 repo.dirstate.forget(f) |
880 if a: | 888 repo.dirstate.setparents(qp, revlog.nullid) |
881 repo.dirstate.forget(a) | 889 self.strip(repo, rev, update=False, backup='strip') |
882 repo.dirstate.setparents(qp, revlog.nullid) | 890 del self.applied[start:end] |
883 self.strip(repo, rev, update=False, backup='strip', wlock=wlock) | 891 if len(self.applied): |
884 del self.applied[start:end] | 892 self.ui.write("Now at: %s\n" % self.applied[-1].name) |
885 if len(self.applied): | 893 else: |
886 self.ui.write("Now at: %s\n" % self.applied[-1].name) | 894 self.ui.write("Patch queue now empty\n") |
887 else: | 895 finally: |
888 self.ui.write("Patch queue now empty\n") | 896 del wlock |
889 | 897 |
890 def diff(self, repo, pats, opts): | 898 def diff(self, repo, pats, opts): |
891 top = self.check_toppatch(repo) | 899 top = self.check_toppatch(repo) |
892 if not top: | 900 if not top: |
893 self.ui.write("No patches applied\n") | 901 self.ui.write("No patches applied\n") |
900 def refresh(self, repo, pats=None, **opts): | 908 def refresh(self, repo, pats=None, **opts): |
901 if len(self.applied) == 0: | 909 if len(self.applied) == 0: |
902 self.ui.write("No patches applied\n") | 910 self.ui.write("No patches applied\n") |
903 return 1 | 911 return 1 |
904 wlock = repo.wlock() | 912 wlock = repo.wlock() |
905 self.check_toppatch(repo) | 913 try: |
906 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name) | 914 self.check_toppatch(repo) |
907 top = revlog.bin(top) | 915 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name) |
908 cparents = repo.changelog.parents(top) | 916 top = revlog.bin(top) |
909 patchparent = self.qparents(repo, top) | 917 cparents = repo.changelog.parents(top) |
910 message, comments, user, date, patchfound = self.readheaders(patchfn) | 918 patchparent = self.qparents(repo, top) |
911 | 919 message, comments, user, date, patchfound = self.readheaders(patchfn) |
912 patchf = self.opener(patchfn, 'r+') | 920 |
913 | 921 patchf = self.opener(patchfn, 'r+') |
914 # if the patch was a git patch, refresh it as a git patch | 922 |
915 for line in patchf: | 923 # if the patch was a git patch, refresh it as a git patch |
916 if line.startswith('diff --git'): | 924 for line in patchf: |
925 if line.startswith('diff --git'): | |
926 self.diffopts().git = True | |
927 break | |
928 patchf.seek(0) | |
929 patchf.truncate() | |
930 | |
931 msg = opts.get('msg', '').rstrip() | |
932 if msg: | |
933 if comments: | |
934 # Remove existing message. | |
935 ci = 0 | |
936 subj = None | |
937 for mi in xrange(len(message)): | |
938 if comments[ci].lower().startswith('subject: '): | |
939 subj = comments[ci][9:] | |
940 while message[mi] != comments[ci] and message[mi] != subj: | |
941 ci += 1 | |
942 del comments[ci] | |
943 comments.append(msg) | |
944 if comments: | |
945 comments = "\n".join(comments) + '\n\n' | |
946 patchf.write(comments) | |
947 | |
948 if opts.get('git'): | |
917 self.diffopts().git = True | 949 self.diffopts().git = True |
918 break | 950 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts) |
919 patchf.seek(0) | 951 tip = repo.changelog.tip() |
920 patchf.truncate() | 952 if top == tip: |
921 | 953 # if the top of our patch queue is also the tip, there is an |
922 msg = opts.get('msg', '').rstrip() | 954 # optimization here. We update the dirstate in place and strip |
923 if msg: | 955 # off the tip commit. Then just commit the current directory |
924 if comments: | 956 # tree. We can also send repo.commit the list of files |
925 # Remove existing message. | 957 # changed to speed up the diff |
926 ci = 0 | 958 # |
927 subj = None | 959 # in short mode, we only diff the files included in the |
928 for mi in xrange(len(message)): | 960 # patch already |
929 if comments[ci].lower().startswith('subject: '): | 961 # |
930 subj = comments[ci][9:] | 962 # this should really read: |
931 while message[mi] != comments[ci] and message[mi] != subj: | 963 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5] |
932 ci += 1 | 964 # but we do it backwards to take advantage of manifest/chlog |
933 del comments[ci] | 965 # caching against the next repo.status call |
934 comments.append(msg) | 966 # |
935 if comments: | 967 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5] |
936 comments = "\n".join(comments) + '\n\n' | 968 changes = repo.changelog.read(tip) |
937 patchf.write(comments) | 969 man = repo.manifest.read(changes[0]) |
938 | 970 aaa = aa[:] |
939 if opts.get('git'): | 971 if opts.get('short'): |
940 self.diffopts().git = True | 972 filelist = mm + aa + dd |
941 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts) | 973 match = dict.fromkeys(filelist).__contains__ |
942 tip = repo.changelog.tip() | 974 else: |
943 if top == tip: | 975 filelist = None |
944 # if the top of our patch queue is also the tip, there is an | 976 match = util.always |
945 # optimization here. We update the dirstate in place and strip | 977 m, a, r, d, u = repo.status(files=filelist, match=match)[:5] |
946 # off the tip commit. Then just commit the current directory | 978 |
947 # tree. We can also send repo.commit the list of files | 979 # we might end up with files that were added between |
948 # changed to speed up the diff | 980 # tip and the dirstate parent, but then changed in the |
949 # | 981 # local dirstate. in this case, we want them to only |
950 # in short mode, we only diff the files included in the | 982 # show up in the added section |
951 # patch already | 983 for x in m: |
952 # | 984 if x not in aa: |
953 # this should really read: | 985 mm.append(x) |
954 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5] | 986 # we might end up with files added by the local dirstate that |
955 # but we do it backwards to take advantage of manifest/chlog | 987 # were deleted by the patch. In this case, they should only |
956 # caching against the next repo.status call | 988 # show up in the changed section. |
957 # | 989 for x in a: |
958 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5] | 990 if x in dd: |
959 changes = repo.changelog.read(tip) | 991 del dd[dd.index(x)] |
960 man = repo.manifest.read(changes[0]) | 992 mm.append(x) |
961 aaa = aa[:] | 993 else: |
962 if opts.get('short'): | 994 aa.append(x) |
963 filelist = mm + aa + dd | 995 # make sure any files deleted in the local dirstate |
964 match = dict.fromkeys(filelist).__contains__ | 996 # are not in the add or change column of the patch |
997 forget = [] | |
998 for x in d + r: | |
999 if x in aa: | |
1000 del aa[aa.index(x)] | |
1001 forget.append(x) | |
1002 continue | |
1003 elif x in mm: | |
1004 del mm[mm.index(x)] | |
1005 dd.append(x) | |
1006 | |
1007 m = util.unique(mm) | |
1008 r = util.unique(dd) | |
1009 a = util.unique(aa) | |
1010 c = [filter(matchfn, l) for l in (m, a, r, [], u)] | |
1011 filelist = util.unique(c[0] + c[1] + c[2]) | |
1012 patch.diff(repo, patchparent, files=filelist, match=matchfn, | |
1013 fp=patchf, changes=c, opts=self.diffopts()) | |
1014 patchf.close() | |
1015 | |
1016 repo.dirstate.setparents(*cparents) | |
1017 copies = {} | |
1018 for dst in a: | |
1019 src = repo.dirstate.copied(dst) | |
1020 if src is None: | |
1021 continue | |
1022 copies.setdefault(src, []).append(dst) | |
1023 repo.dirstate.add(dst) | |
1024 # remember the copies between patchparent and tip | |
1025 # this may be slow, so don't do it if we're not tracking copies | |
1026 if self.diffopts().git: | |
1027 for dst in aaa: | |
1028 f = repo.file(dst) | |
1029 src = f.renamed(man[dst]) | |
1030 if src: | |
1031 copies[src[0]] = copies.get(dst, []) | |
1032 if dst in a: | |
1033 copies[src[0]].append(dst) | |
1034 # we can't copy a file created by the patch itself | |
1035 if dst in copies: | |
1036 del copies[dst] | |
1037 for src, dsts in copies.iteritems(): | |
1038 for dst in dsts: | |
1039 repo.dirstate.copy(src, dst) | |
1040 for f in r: | |
1041 repo.dirstate.remove(f) | |
1042 # if the patch excludes a modified file, mark that | |
1043 # file with mtime=0 so status can see it. | |
1044 mm = [] | |
1045 for i in xrange(len(m)-1, -1, -1): | |
1046 if not matchfn(m[i]): | |
1047 mm.append(m[i]) | |
1048 del m[i] | |
1049 for f in m: | |
1050 repo.dirstate.normal(f) | |
1051 for f in mm: | |
1052 repo.dirstate.normaldirty(f) | |
1053 for f in forget: | |
1054 repo.dirstate.forget(f) | |
1055 | |
1056 if not msg: | |
1057 if not message: | |
1058 message = "[mq]: %s\n" % patchfn | |
1059 else: | |
1060 message = "\n".join(message) | |
1061 else: | |
1062 message = msg | |
1063 | |
1064 self.strip(repo, top, update=False, | |
1065 backup='strip') | |
1066 n = repo.commit(filelist, message, changes[1], match=matchfn, | |
1067 force=1) | |
1068 self.applied[-1] = statusentry(revlog.hex(n), patchfn) | |
1069 self.applied_dirty = 1 | |
1070 self.removeundo(repo) | |
965 else: | 1071 else: |
966 filelist = None | 1072 self.printdiff(repo, patchparent, fp=patchf) |
967 match = util.always | 1073 patchf.close() |
968 m, a, r, d, u = repo.status(files=filelist, match=match)[:5] | 1074 added = repo.status()[1] |
969 | 1075 for a in added: |
970 # we might end up with files that were added between tip and | 1076 f = repo.wjoin(a) |
971 # the dirstate parent, but then changed in the local dirstate. | 1077 try: |
972 # in this case, we want them to only show up in the added section | 1078 os.unlink(f) |
973 for x in m: | 1079 except OSError, e: |
974 if x not in aa: | 1080 if e.errno != errno.ENOENT: |
975 mm.append(x) | 1081 raise |
976 # we might end up with files added by the local dirstate that | 1082 try: os.removedirs(os.path.dirname(f)) |
977 # were deleted by the patch. In this case, they should only | 1083 except: pass |
978 # show up in the changed section. | 1084 # forget the file copies in the dirstate |
979 for x in a: | 1085 # push should readd the files later on |
980 if x in dd: | 1086 repo.dirstate.forget(a) |
981 del dd[dd.index(x)] | 1087 self.pop(repo, force=True) |
982 mm.append(x) | 1088 self.push(repo, force=True) |
983 else: | 1089 finally: |
984 aa.append(x) | 1090 del wlock |
985 # make sure any files deleted in the local dirstate | |
986 # are not in the add or change column of the patch | |
987 forget = [] | |
988 for x in d + r: | |
989 if x in aa: | |
990 del aa[aa.index(x)] | |
991 forget.append(x) | |
992 continue | |
993 elif x in mm: | |
994 del mm[mm.index(x)] | |
995 dd.append(x) | |
996 | |
997 m = util.unique(mm) | |
998 r = util.unique(dd) | |
999 a = util.unique(aa) | |
1000 c = [filter(matchfn, l) for l in (m, a, r, [], u)] | |
1001 filelist = util.unique(c[0] + c[1] + c[2]) | |
1002 patch.diff(repo, patchparent, files=filelist, match=matchfn, | |
1003 fp=patchf, changes=c, opts=self.diffopts()) | |
1004 patchf.close() | |
1005 | |
1006 repo.dirstate.setparents(*cparents) | |
1007 copies = {} | |
1008 for dst in a: | |
1009 src = repo.dirstate.copied(dst) | |
1010 if src is None: | |
1011 continue | |
1012 copies.setdefault(src, []).append(dst) | |
1013 repo.dirstate.update(a, 'a') | |
1014 # remember the copies between patchparent and tip | |
1015 # this may be slow, so don't do it if we're not tracking copies | |
1016 if self.diffopts().git: | |
1017 for dst in aaa: | |
1018 f = repo.file(dst) | |
1019 src = f.renamed(man[dst]) | |
1020 if src: | |
1021 copies[src[0]] = copies.get(dst, []) | |
1022 if dst in a: | |
1023 copies[src[0]].append(dst) | |
1024 # we can't copy a file created by the patch itself | |
1025 if dst in copies: | |
1026 del copies[dst] | |
1027 for src, dsts in copies.iteritems(): | |
1028 for dst in dsts: | |
1029 repo.dirstate.copy(src, dst) | |
1030 repo.dirstate.update(r, 'r') | |
1031 # if the patch excludes a modified file, mark that file with mtime=0 | |
1032 # so status can see it. | |
1033 mm = [] | |
1034 for i in xrange(len(m)-1, -1, -1): | |
1035 if not matchfn(m[i]): | |
1036 mm.append(m[i]) | |
1037 del m[i] | |
1038 repo.dirstate.update(m, 'n') | |
1039 repo.dirstate.update(mm, 'n', st_mtime=-1, st_size=-1) | |
1040 repo.dirstate.forget(forget) | |
1041 | |
1042 if not msg: | |
1043 if not message: | |
1044 message = "[mq]: %s\n" % patchfn | |
1045 else: | |
1046 message = "\n".join(message) | |
1047 else: | |
1048 message = msg | |
1049 | |
1050 self.strip(repo, top, update=False, backup='strip', wlock=wlock) | |
1051 n = repo.commit(filelist, message, changes[1], match=matchfn, | |
1052 force=1, wlock=wlock) | |
1053 self.applied[-1] = statusentry(revlog.hex(n), patchfn) | |
1054 self.applied_dirty = 1 | |
1055 self.removeundo(repo) | |
1056 else: | |
1057 self.printdiff(repo, patchparent, fp=patchf) | |
1058 patchf.close() | |
1059 added = repo.status()[1] | |
1060 for a in added: | |
1061 f = repo.wjoin(a) | |
1062 try: | |
1063 os.unlink(f) | |
1064 except OSError, e: | |
1065 if e.errno != errno.ENOENT: | |
1066 raise | |
1067 try: os.removedirs(os.path.dirname(f)) | |
1068 except: pass | |
1069 # forget the file copies in the dirstate | |
1070 # push should readd the files later on | |
1071 repo.dirstate.forget(added) | |
1072 self.pop(repo, force=True, wlock=wlock) | |
1073 self.push(repo, force=True, wlock=wlock) | |
1074 | 1091 |
1075 def init(self, repo, create=False): | 1092 def init(self, repo, create=False): |
1076 if not create and os.path.isdir(self.path): | 1093 if not create and os.path.isdir(self.path): |
1077 raise util.Abort(_("patch queue directory already exists")) | 1094 raise util.Abort(_("patch queue directory already exists")) |
1078 try: | 1095 try: |
1485 applied in destination. If you clone remote repository, be sure | 1502 applied in destination. If you clone remote repository, be sure |
1486 before that it has no patches applied. | 1503 before that it has no patches applied. |
1487 | 1504 |
1488 Source patch repository is looked for in <src>/.hg/patches by | 1505 Source patch repository is looked for in <src>/.hg/patches by |
1489 default. Use -p <url> to change. | 1506 default. Use -p <url> to change. |
1507 | |
1508 The patch directory must be a nested mercurial repository, as | |
1509 would be created by qinit -c. | |
1490 ''' | 1510 ''' |
1491 cmdutil.setremoteconfig(ui, opts) | 1511 cmdutil.setremoteconfig(ui, opts) |
1492 if dest is None: | 1512 if dest is None: |
1493 dest = hg.defaultdest(source) | 1513 dest = hg.defaultdest(source) |
1494 sr = hg.repository(ui, ui.expandpath(source)) | 1514 sr = hg.repository(ui, ui.expandpath(source)) |
1515 patchdir = opts['patches'] or (sr.url() + '/.hg/patches') | |
1516 try: | |
1517 pr = hg.repository(ui, patchdir) | |
1518 except hg.RepoError: | |
1519 raise util.Abort(_('versioned patch repository not found' | |
1520 ' (see qinit -c)')) | |
1495 qbase, destrev = None, None | 1521 qbase, destrev = None, None |
1496 if sr.local(): | 1522 if sr.local(): |
1497 if sr.mq.applied: | 1523 if sr.mq.applied: |
1498 qbase = revlog.bin(sr.mq.applied[0].rev) | 1524 qbase = revlog.bin(sr.mq.applied[0].rev) |
1499 if not hg.islocal(dest): | 1525 if not hg.islocal(dest): |
1855 | 1881 |
1856 util.rename(q.join(patch), absdest) | 1882 util.rename(q.join(patch), absdest) |
1857 r = q.qrepo() | 1883 r = q.qrepo() |
1858 if r: | 1884 if r: |
1859 wlock = r.wlock() | 1885 wlock = r.wlock() |
1860 if r.dirstate.state(name) == 'r': | 1886 try: |
1861 r.undelete([name], wlock) | 1887 if r.dirstate[name] == 'r': |
1862 r.copy(patch, name, wlock) | 1888 r.undelete([name]) |
1863 r.remove([patch], False, wlock) | 1889 r.copy(patch, name) |
1890 r.remove([patch], False) | |
1891 finally: | |
1892 del wlock | |
1864 | 1893 |
1865 q.save_dirty() | 1894 q.save_dirty() |
1866 | 1895 |
1867 def restore(ui, repo, rev, **opts): | 1896 def restore(ui, repo, rev, **opts): |
1868 """restore the queue state saved by a rev""" | 1897 """restore the queue state saved by a rev""" |
2100 (clone, | 2129 (clone, |
2101 [('', 'pull', None, _('use pull protocol to copy metadata')), | 2130 [('', 'pull', None, _('use pull protocol to copy metadata')), |
2102 ('U', 'noupdate', None, _('do not update the new working directories')), | 2131 ('U', 'noupdate', None, _('do not update the new working directories')), |
2103 ('', 'uncompressed', None, | 2132 ('', 'uncompressed', None, |
2104 _('use uncompressed transfer (fast over LAN)')), | 2133 _('use uncompressed transfer (fast over LAN)')), |
2105 ('e', 'ssh', '', _('specify ssh command to use')), | |
2106 ('p', 'patches', '', _('location of source patch repo')), | 2134 ('p', 'patches', '', _('location of source patch repo')), |
2107 ('', 'remotecmd', '', | 2135 ] + commands.remoteopts, |
2108 _('specify hg command to run on the remote side'))], | |
2109 _('hg qclone [OPTION]... SOURCE [DEST]')), | 2136 _('hg qclone [OPTION]... SOURCE [DEST]')), |
2110 "qcommit|qci": | 2137 "qcommit|qci": |
2111 (commit, | 2138 (commit, |
2112 commands.table["^commit|ci"][1], | 2139 commands.table["^commit|ci"][1], |
2113 _('hg qcommit [OPTION]... [FILE]...')), | 2140 _('hg qcommit [OPTION]... [FILE]...')), |
2114 "^qdiff": | 2141 "^qdiff": |
2115 (diff, | 2142 (diff, |
2116 [('g', 'git', None, _('use git extended diff format')), | 2143 [('g', 'git', None, _('use git extended diff format')), |
2117 ('I', 'include', [], _('include names matching the given patterns')), | 2144 ] + commands.walkopts, |
2118 ('X', 'exclude', [], _('exclude names matching the given patterns'))], | |
2119 _('hg qdiff [-I] [-X] [-g] [FILE]...')), | 2145 _('hg qdiff [-I] [-X] [-g] [FILE]...')), |
2120 "qdelete|qremove|qrm": | 2146 "qdelete|qremove|qrm": |
2121 (delete, | 2147 (delete, |
2122 [('k', 'keep', None, _('keep patch file')), | 2148 [('k', 'keep', None, _('keep patch file')), |
2123 ('r', 'rev', [], _('stop managing a revision'))], | 2149 ('r', 'rev', [], _('stop managing a revision'))], |
2152 _('hg qinit [-c]')), | 2178 _('hg qinit [-c]')), |
2153 "qnew": | 2179 "qnew": |
2154 (new, | 2180 (new, |
2155 [('e', 'edit', None, _('edit commit message')), | 2181 [('e', 'edit', None, _('edit commit message')), |
2156 ('f', 'force', None, _('import uncommitted changes into patch')), | 2182 ('f', 'force', None, _('import uncommitted changes into patch')), |
2157 ('I', 'include', [], _('include names matching the given patterns')), | 2183 ('g', 'git', None, _('use git extended diff format')), |
2158 ('X', 'exclude', [], _('exclude names matching the given patterns')), | 2184 ] + commands.walkopts + commands.commitopts, |
2159 ] + commands.commitopts, | |
2160 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')), | 2185 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')), |
2161 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')), | 2186 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')), |
2162 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')), | 2187 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')), |
2163 "^qpop": | 2188 "^qpop": |
2164 (pop, | 2189 (pop, |
2177 "^qrefresh": | 2202 "^qrefresh": |
2178 (refresh, | 2203 (refresh, |
2179 [('e', 'edit', None, _('edit commit message')), | 2204 [('e', 'edit', None, _('edit commit message')), |
2180 ('g', 'git', None, _('use git extended diff format')), | 2205 ('g', 'git', None, _('use git extended diff format')), |
2181 ('s', 'short', None, _('refresh only files already in the patch')), | 2206 ('s', 'short', None, _('refresh only files already in the patch')), |
2182 ('I', 'include', [], _('include names matching the given patterns')), | 2207 ] + commands.walkopts + commands.commitopts, |
2183 ('X', 'exclude', [], _('exclude names matching the given patterns')), | |
2184 ] + commands.commitopts, | |
2185 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')), | 2208 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')), |
2186 'qrename|qmv': | 2209 'qrename|qmv': |
2187 (rename, [], _('hg qrename PATCH1 [PATCH2]')), | 2210 (rename, [], _('hg qrename PATCH1 [PATCH2]')), |
2188 "qrestore": | 2211 "qrestore": |
2189 (restore, | 2212 (restore, |