hgext/mq.py
changeset 5124 06154aff2b1a
parent 4911 fc502517d68d
parent 5122 c80af96943aa
child 5140 f6c520fd70cf
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,