hgext/mq.py
changeset 4712 f49fcbb325bc
parent 4711 c71bf1d251ad
parent 4702 18e91c9def0c
child 4713 c29ee52e0b68
equal deleted inserted replaced
4711:c71bf1d251ad 4712:f49fcbb325bc
    28 remove patch from applied stack           qpop
    28 remove patch from applied stack           qpop
    29 refresh contents of top applied patch     qrefresh
    29 refresh contents of top applied patch     qrefresh
    30 '''
    30 '''
    31 
    31 
    32 from mercurial.i18n import _
    32 from mercurial.i18n import _
    33 from mercurial import commands, cmdutil, hg, patch, revlog, util, changegroup
    33 from mercurial import commands, cmdutil, hg, patch, revlog, util
       
    34 from mercurial import repair
    34 import os, sys, re, errno
    35 import os, sys, re, errno
    35 
    36 
    36 commands.norepo += " qclone qversion"
    37 commands.norepo += " qclone qversion"
    37 
    38 
    38 # Patch names looks like unix-file names.
    39 # Patch names looks like unix-file names.
   627         if commitfiles:
   628         if commitfiles:
   628             self.refresh(repo, short=True)
   629             self.refresh(repo, short=True)
   629         self.removeundo(repo)
   630         self.removeundo(repo)
   630 
   631 
   631     def strip(self, repo, rev, update=True, backup="all", wlock=None):
   632     def strip(self, repo, rev, update=True, backup="all", wlock=None):
   632         def limitheads(chlog, stop):
       
   633             """return the list of all nodes that have no children"""
       
   634             p = {}
       
   635             h = []
       
   636             stoprev = 0
       
   637             if stop in chlog.nodemap:
       
   638                 stoprev = chlog.rev(stop)
       
   639 
       
   640             for r in xrange(chlog.count() - 1, -1, -1):
       
   641                 n = chlog.node(r)
       
   642                 if n not in p:
       
   643                     h.append(n)
       
   644                 if n == stop:
       
   645                     break
       
   646                 if r < stoprev:
       
   647                     break
       
   648                 for pn in chlog.parents(n):
       
   649                     p[pn] = 1
       
   650             return h
       
   651 
       
   652         def bundle(cg):
       
   653             backupdir = repo.join("strip-backup")
       
   654             if not os.path.isdir(backupdir):
       
   655                 os.mkdir(backupdir)
       
   656             name = os.path.join(backupdir, "%s" % revlog.short(rev))
       
   657             name = savename(name)
       
   658             self.ui.warn("saving bundle to %s\n" % name)
       
   659             return changegroup.writebundle(cg, name, "HG10BZ")
       
   660 
       
   661         def stripall(revnum):
       
   662             mm = repo.changectx(rev).manifest()
       
   663             seen = {}
       
   664 
       
   665             for x in xrange(revnum, repo.changelog.count()):
       
   666                 for f in repo.changectx(x).files():
       
   667                     if f in seen:
       
   668                         continue
       
   669                     seen[f] = 1
       
   670                     if f in mm:
       
   671                         filerev = mm[f]
       
   672                     else:
       
   673                         filerev = 0
       
   674                     seen[f] = filerev
       
   675             # we go in two steps here so the strip loop happens in a
       
   676             # sensible order.  When stripping many files, this helps keep
       
   677             # our disk access patterns under control.
       
   678             seen_list = seen.keys()
       
   679             seen_list.sort()
       
   680             for f in seen_list:
       
   681                 ff = repo.file(f)
       
   682                 filerev = seen[f]
       
   683                 if filerev != 0:
       
   684                     if filerev in ff.nodemap:
       
   685                         filerev = ff.rev(filerev)
       
   686                     else:
       
   687                         filerev = 0
       
   688                 ff.strip(filerev, revnum)
       
   689 
       
   690         if not wlock:
   633         if not wlock:
   691             wlock = repo.wlock()
   634             wlock = repo.wlock()
   692         lock = repo.lock()
   635         lock = repo.lock()
   693         chlog = repo.changelog
       
   694         # TODO delete the undo files, and handle undo of merge sets
       
   695         pp = chlog.parents(rev)
       
   696         revnum = chlog.rev(rev)
       
   697 
   636 
   698         if update:
   637         if update:
   699             self.check_localchanges(repo, refresh=False)
   638             self.check_localchanges(repo, refresh=False)
   700             urev = self.qparents(repo, rev)
   639             urev = self.qparents(repo, rev)
   701             hg.clean(repo, urev, wlock=wlock)
   640             hg.clean(repo, urev, wlock=wlock)
   702             repo.dirstate.write()
   641             repo.dirstate.write()
   703 
   642 
   704         # save is a list of all the branches we are truncating away
       
   705         # that we actually want to keep.  changegroup will be used
       
   706         # to preserve them and add them back after the truncate
       
   707         saveheads = []
       
   708         savebases = {}
       
   709 
       
   710         heads = limitheads(chlog, rev)
       
   711         seen = {}
       
   712 
       
   713         # search through all the heads, finding those where the revision
       
   714         # we want to strip away is an ancestor.  Also look for merges
       
   715         # that might be turned into new heads by the strip.
       
   716         while heads:
       
   717             h = heads.pop()
       
   718             n = h
       
   719             while True:
       
   720                 seen[n] = 1
       
   721                 pp = chlog.parents(n)
       
   722                 if pp[1] != revlog.nullid:
       
   723                     for p in pp:
       
   724                         if chlog.rev(p) > revnum and p not in seen:
       
   725                             heads.append(p)
       
   726                 if pp[0] == revlog.nullid:
       
   727                     break
       
   728                 if chlog.rev(pp[0]) < revnum:
       
   729                     break
       
   730                 n = pp[0]
       
   731                 if n == rev:
       
   732                     break
       
   733             r = chlog.reachable(h, rev)
       
   734             if rev not in r:
       
   735                 saveheads.append(h)
       
   736                 for x in r:
       
   737                     if chlog.rev(x) > revnum:
       
   738                         savebases[x] = 1
       
   739 
       
   740         # create a changegroup for all the branches we need to keep
       
   741         if backup == "all":
       
   742             backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
       
   743             bundle(backupch)
       
   744         if saveheads:
       
   745             backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
       
   746             chgrpfile = bundle(backupch)
       
   747 
       
   748         stripall(revnum)
       
   749 
       
   750         change = chlog.read(rev)
       
   751         chlog.strip(revnum, revnum)
       
   752         repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
       
   753         self.removeundo(repo)
   643         self.removeundo(repo)
   754         if saveheads:
   644         repair.strip(self.ui, repo, rev, backup)
   755             self.ui.status("adding branch\n")
       
   756             commands.unbundle(self.ui, repo, "file:%s" % chgrpfile,
       
   757                               update=False)
       
   758             if backup != "strip":
       
   759                 os.unlink(chgrpfile)
       
   760 
   645 
   761     def isapplied(self, patch):
   646     def isapplied(self, patch):
   762         """returns (index, rev, patch)"""
   647         """returns (index, rev, patch)"""
   763         for i in xrange(len(self.applied)):
   648         for i in xrange(len(self.applied)):
   764             a = self.applied[i]
   649             a = self.applied[i]