comparison hgext/mq.py @ 5124: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
5123:f94dbc6c7eaf 5124: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,