hgext/mq.py
changeset 4700 a2e025fcd256
parent 4614 3a645af7fb76
child 4701 d2da07fb5727
equal deleted inserted replaced
4699:a6b62584d0b2 4700:a2e025fcd256
    36 commands.norepo += " qclone qversion"
    36 commands.norepo += " qclone qversion"
    37 
    37 
    38 # Patch names looks like unix-file names.
    38 # Patch names looks like unix-file names.
    39 # They must be joinable with queue directory and result in the patch path.
    39 # They must be joinable with queue directory and result in the patch path.
    40 normname = util.normpath
    40 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(cg):
       
    64         backupdir = repo.join("strip-backup")
       
    65         if not os.path.isdir(backupdir):
       
    66             os.mkdir(backupdir)
       
    67         name = os.path.join(backupdir, "%s" % revlog.short(rev))
       
    68         name = savename(name)
       
    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         backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
       
   145         bundle(backupch)
       
   146     if saveheads:
       
   147         backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
       
   148         chgrpfile = bundle(backupch)
       
   149 
       
   150     stripall(revnum)
       
   151 
       
   152     change = chlog.read(rev)
       
   153     chlog.strip(revnum, revnum)
       
   154     repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
       
   155     if saveheads:
       
   156         ui.status("adding branch\n")
       
   157         commands.unbundle(ui, repo, "file:%s" % chgrpfile, update=False)
       
   158         if backup != "strip":
       
   159             os.unlink(chgrpfile)
    41 
   160 
    42 class statusentry:
   161 class statusentry:
    43     def __init__(self, rev, name=None):
   162     def __init__(self, rev, name=None):
    44         if not name:
   163         if not name:
    45             fields = rev.split(':', 1)
   164             fields = rev.split(':', 1)
   627         if commitfiles:
   746         if commitfiles:
   628             self.refresh(repo, short=True)
   747             self.refresh(repo, short=True)
   629         self.removeundo(repo)
   748         self.removeundo(repo)
   630 
   749 
   631     def strip(self, repo, rev, update=True, backup="all", wlock=None):
   750     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:
   751         if not wlock:
   691             wlock = repo.wlock()
   752             wlock = repo.wlock()
   692         lock = repo.lock()
   753         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 
   754 
   698         if update:
   755         if update:
   699             self.check_localchanges(repo, refresh=False)
   756             self.check_localchanges(repo, refresh=False)
   700             urev = self.qparents(repo, rev)
   757             urev = self.qparents(repo, rev)
   701             hg.clean(repo, urev, wlock=wlock)
   758             hg.clean(repo, urev, wlock=wlock)
   702             repo.dirstate.write()
   759             repo.dirstate.write()
   703 
   760 
   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)
   761         self.removeundo(repo)
   754         if saveheads:
   762         _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 
   763 
   761     def isapplied(self, patch):
   764     def isapplied(self, patch):
   762         """returns (index, rev, patch)"""
   765         """returns (index, rev, patch)"""
   763         for i in xrange(len(self.applied)):
   766         for i in xrange(len(self.applied)):
   764             a = self.applied[i]
   767             a = self.applied[i]