hgext/mq.py
changeset 4702 18e91c9def0c
parent 4701 d2da07fb5727
child 4712 f49fcbb325bc
equal deleted inserted replaced
4701:d2da07fb5727 4702:18e91c9def0c
    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.
    39 # They must be joinable with queue directory and result in the patch path.
    40 # They must be joinable with queue directory and result in the patch path.
    40 normname = util.normpath
    41 normname = util.normpath
    41 
       
    42 def _strip(ui, repo, rev, backup="all"):
       
    43     def limitheads(chlog, stop):
       
    44         """return the list of all nodes that have no children"""
       
    45         p = {}
       
    46         h = []
       
    47         stoprev = 0
       
    48         if stop in chlog.nodemap:
       
    49             stoprev = chlog.rev(stop)
       
    50 
       
    51         for r in xrange(chlog.count() - 1, -1, -1):
       
    52             n = chlog.node(r)
       
    53             if n not in p:
       
    54                 h.append(n)
       
    55             if n == stop:
       
    56                 break
       
    57             if r < stoprev:
       
    58                 break
       
    59             for pn in chlog.parents(n):
       
    60                 p[pn] = 1
       
    61         return h
       
    62 
       
    63     def bundle(repo, bases, heads, rev, suffix):
       
    64         cg = repo.changegroupsubset(bases, heads, 'strip')
       
    65         backupdir = repo.join("strip-backup")
       
    66         if not os.path.isdir(backupdir):
       
    67             os.mkdir(backupdir)
       
    68         name = os.path.join(backupdir, "%s-%s" % (revlog.short(rev), suffix))
       
    69         ui.warn("saving bundle to %s\n" % name)
       
    70         return changegroup.writebundle(cg, name, "HG10BZ")
       
    71 
       
    72     def stripall(revnum):
       
    73         mm = repo.changectx(rev).manifest()
       
    74         seen = {}
       
    75 
       
    76         for x in xrange(revnum, repo.changelog.count()):
       
    77             for f in repo.changectx(x).files():
       
    78                 if f in seen:
       
    79                     continue
       
    80                 seen[f] = 1
       
    81                 if f in mm:
       
    82                     filerev = mm[f]
       
    83                 else:
       
    84                     filerev = 0
       
    85                 seen[f] = filerev
       
    86         # we go in two steps here so the strip loop happens in a
       
    87         # sensible order.  When stripping many files, this helps keep
       
    88         # our disk access patterns under control.
       
    89         seen_list = seen.keys()
       
    90         seen_list.sort()
       
    91         for f in seen_list:
       
    92             ff = repo.file(f)
       
    93             filerev = seen[f]
       
    94             if filerev != 0:
       
    95                 if filerev in ff.nodemap:
       
    96                     filerev = ff.rev(filerev)
       
    97                 else:
       
    98                     filerev = 0
       
    99             ff.strip(filerev, revnum)
       
   100 
       
   101     chlog = repo.changelog
       
   102     # TODO delete the undo files, and handle undo of merge sets
       
   103     pp = chlog.parents(rev)
       
   104     revnum = chlog.rev(rev)
       
   105 
       
   106     # save is a list of all the branches we are truncating away
       
   107     # that we actually want to keep.  changegroup will be used
       
   108     # to preserve them and add them back after the truncate
       
   109     saveheads = []
       
   110     savebases = {}
       
   111 
       
   112     heads = limitheads(chlog, rev)
       
   113     seen = {}
       
   114 
       
   115     # search through all the heads, finding those where the revision
       
   116     # we want to strip away is an ancestor.  Also look for merges
       
   117     # that might be turned into new heads by the strip.
       
   118     while heads:
       
   119         h = heads.pop()
       
   120         n = h
       
   121         while True:
       
   122             seen[n] = 1
       
   123             pp = chlog.parents(n)
       
   124             if pp[1] != revlog.nullid:
       
   125                 for p in pp:
       
   126                     if chlog.rev(p) > revnum and p not in seen:
       
   127                         heads.append(p)
       
   128             if pp[0] == revlog.nullid:
       
   129                 break
       
   130             if chlog.rev(pp[0]) < revnum:
       
   131                 break
       
   132             n = pp[0]
       
   133             if n == rev:
       
   134                 break
       
   135         r = chlog.reachable(h, rev)
       
   136         if rev not in r:
       
   137             saveheads.append(h)
       
   138             for x in r:
       
   139                 if chlog.rev(x) > revnum:
       
   140                     savebases[x] = 1
       
   141 
       
   142     # create a changegroup for all the branches we need to keep
       
   143     if backup == "all":
       
   144         bundle(repo, [rev], chlog.heads(), rev, 'backup')
       
   145     if saveheads:
       
   146         chgrpfile = bundle(repo, savebases.keys(), saveheads, rev, 'temp')
       
   147 
       
   148     stripall(revnum)
       
   149 
       
   150     change = chlog.read(rev)
       
   151     chlog.strip(revnum, revnum)
       
   152     repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
       
   153     if saveheads:
       
   154         ui.status("adding branch\n")
       
   155         commands.unbundle(ui, repo, "file:%s" % chgrpfile, update=False)
       
   156         if backup != "strip":
       
   157             os.unlink(chgrpfile)
       
   158 
    42 
   159 class statusentry:
    43 class statusentry:
   160     def __init__(self, rev, name=None):
    44     def __init__(self, rev, name=None):
   161         if not name:
    45         if not name:
   162             fields = rev.split(':', 1)
    46             fields = rev.split(':', 1)
   755             urev = self.qparents(repo, rev)
   639             urev = self.qparents(repo, rev)
   756             hg.clean(repo, urev, wlock=wlock)
   640             hg.clean(repo, urev, wlock=wlock)
   757             repo.dirstate.write()
   641             repo.dirstate.write()
   758 
   642 
   759         self.removeundo(repo)
   643         self.removeundo(repo)
   760         _strip(self.ui, repo, rev, backup)
   644         repair.strip(self.ui, repo, rev, backup)
   761 
   645 
   762     def isapplied(self, patch):
   646     def isapplied(self, patch):
   763         """returns (index, rev, patch)"""
   647         """returns (index, rev, patch)"""
   764         for i in xrange(len(self.applied)):
   648         for i in xrange(len(self.applied)):
   765             a = self.applied[i]
   649             a = self.applied[i]