hgext/mq.py
changeset 16654 490ed8972f1b
parent 16653 73b8c2554be8
child 16655 6ca125af882f
equal deleted inserted replaced
16653:73b8c2554be8 16654:490ed8972f1b
   278     finally:
   278     finally:
   279         repo._committingpatch = False
   279         repo._committingpatch = False
   280         if phase is not None:
   280         if phase is not None:
   281             repo.ui.restoreconfig(backup)
   281             repo.ui.restoreconfig(backup)
   282 
   282 
       
   283 class AbortNoCleanup(error.Abort):
       
   284     pass
       
   285 
   283 class queue(object):
   286 class queue(object):
   284     def __init__(self, ui, path, patchdir=None):
   287     def __init__(self, ui, path, patchdir=None):
   285         self.basepath = path
   288         self.basepath = path
   286         try:
   289         try:
   287             fh = open(os.path.join(path, 'patches.queue'))
   290             fh = open(os.path.join(path, 'patches.queue'))
   679             self.ui.traceback()
   682             self.ui.traceback()
   680             return (False, list(files), False)
   683             return (False, list(files), False)
   681 
   684 
   682     def apply(self, repo, series, list=False, update_status=True,
   685     def apply(self, repo, series, list=False, update_status=True,
   683               strict=False, patchdir=None, merge=None, all_files=None,
   686               strict=False, patchdir=None, merge=None, all_files=None,
   684               tobackup=None):
   687               tobackup=None, check=False):
   685         wlock = lock = tr = None
   688         wlock = lock = tr = None
   686         try:
   689         try:
   687             wlock = repo.wlock()
   690             wlock = repo.wlock()
   688             lock = repo.lock()
   691             lock = repo.lock()
   689             tr = repo.transaction("qpush")
   692             tr = repo.transaction("qpush")
   690             try:
   693             try:
   691                 ret = self._apply(repo, series, list, update_status,
   694                 ret = self._apply(repo, series, list, update_status,
   692                                   strict, patchdir, merge, all_files=all_files,
   695                                   strict, patchdir, merge, all_files=all_files,
   693                                   tobackup=tobackup)
   696                                   tobackup=tobackup, check=check)
   694                 tr.close()
   697                 tr.close()
   695                 self.savedirty()
   698                 self.savedirty()
   696                 return ret
   699                 return ret
       
   700             except AbortNoCleanup:
       
   701                 tr.close()
       
   702                 self.savedirty()
       
   703                 return 2, repo.dirstate.p1()
   697             except:
   704             except:
   698                 try:
   705                 try:
   699                     tr.abort()
   706                     tr.abort()
   700                 finally:
   707                 finally:
   701                     repo.invalidate()
   708                     repo.invalidate()
   706             release(tr, lock, wlock)
   713             release(tr, lock, wlock)
   707             self.removeundo(repo)
   714             self.removeundo(repo)
   708 
   715 
   709     def _apply(self, repo, series, list=False, update_status=True,
   716     def _apply(self, repo, series, list=False, update_status=True,
   710                strict=False, patchdir=None, merge=None, all_files=None,
   717                strict=False, patchdir=None, merge=None, all_files=None,
   711                tobackup=None):
   718                tobackup=None, check=False):
   712         """returns (error, hash)
   719         """returns (error, hash)
   713 
   720 
   714         error = 1 for unable to read, 2 for patch failed, 3 for patch
   721         error = 1 for unable to read, 2 for patch failed, 3 for patch
   715         fuzz. tobackup is None or a set of files to backup before they
   722         fuzz. tobackup is None or a set of files to backup before they
   716         are modified by a patch.
   723         are modified by a patch.
   747 
   754 
   748             if ph.haspatch:
   755             if ph.haspatch:
   749                 if tobackup:
   756                 if tobackup:
   750                     touched = patchmod.changedfiles(self.ui, repo, pf)
   757                     touched = patchmod.changedfiles(self.ui, repo, pf)
   751                     touched = set(touched) & tobackup
   758                     touched = set(touched) & tobackup
       
   759                     if touched and check:
       
   760                         raise AbortNoCleanup(
       
   761                             _("local changes found, refresh first"))
   752                     self.backup(repo, touched, copy=True)
   762                     self.backup(repo, touched, copy=True)
   753                     tobackup = tobackup - touched
   763                     tobackup = tobackup - touched
   754                 (patcherr, files, fuzz) = self.patch(repo, pf)
   764                 (patcherr, files, fuzz) = self.patch(repo, pf)
   755                 if all_files is not None:
   765                 if all_files is not None:
   756                     all_files.update(files)
   766                     all_files.update(files)
   956             if os.path.isdir(self.join(name)):
   966             if os.path.isdir(self.join(name)):
   957                 raise util.Abort(_('"%s" already exists as a directory')
   967                 raise util.Abort(_('"%s" already exists as a directory')
   958                                  % name)
   968                                  % name)
   959             else:
   969             else:
   960                 raise util.Abort(_('patch "%s" already exists') % name)
   970                 raise util.Abort(_('patch "%s" already exists') % name)
       
   971 
       
   972     def checkforcecheck(self, check, force):
       
   973         if force and check:
       
   974             raise util.Abort(_('cannot use both --force and --check'))
   961 
   975 
   962     def new(self, repo, patchfn, *pats, **opts):
   976     def new(self, repo, patchfn, *pats, **opts):
   963         """options:
   977         """options:
   964            msg: a string or a no-argument function returning a string
   978            msg: a string or a no-argument function returning a string
   965         """
   979         """
  1154                         else:
  1168                         else:
  1155                             if i + off < len(self.series):
  1169                             if i + off < len(self.series):
  1156                                 return self.series[i + off]
  1170                                 return self.series[i + off]
  1157         raise util.Abort(_("patch %s not in series") % patch)
  1171         raise util.Abort(_("patch %s not in series") % patch)
  1158 
  1172 
  1159     def push(self, repo, patch=None, force=False, list=False,
  1173     def push(self, repo, patch=None, force=False, list=False, mergeq=None,
  1160              mergeq=None, all=False, move=False, exact=False, nobackup=False):
  1174              all=False, move=False, exact=False, nobackup=False, check=False):
       
  1175         self.checkforcecheck(check, force)
  1161         diffopts = self.diffopts()
  1176         diffopts = self.diffopts()
  1162         wlock = repo.wlock()
  1177         wlock = repo.wlock()
  1163         try:
  1178         try:
  1164             heads = []
  1179             heads = []
  1165             for b, ls in repo.branchmap().iteritems():
  1180             for b, ls in repo.branchmap().iteritems():
  1210             # work as it detects an error when done
  1225             # work as it detects an error when done
  1211             start = self.seriesend()
  1226             start = self.seriesend()
  1212             if start == len(self.series):
  1227             if start == len(self.series):
  1213                 self.ui.warn(_('patch series already fully applied\n'))
  1228                 self.ui.warn(_('patch series already fully applied\n'))
  1214                 return 1
  1229                 return 1
  1215             if not force:
  1230             if not force and not check:
  1216                 self.checklocalchanges(repo, refresh=self.applied)
  1231                 self.checklocalchanges(repo, refresh=self.applied)
  1217 
  1232 
  1218             if exact:
  1233             if exact:
       
  1234                 if check:
       
  1235                     raise util.Abort(
       
  1236                         _("cannot use --exact and --check together"))
  1219                 if move:
  1237                 if move:
  1220                     raise util.Abort(_("cannot use --exact and --move together"))
  1238                     raise util.Abort(_("cannot use --exact and --move together"))
  1221                 if self.applied:
  1239                 if self.applied:
  1222                     raise util.Abort(_("cannot push --exact with applied patches"))
  1240                     raise util.Abort(_("cannot push --exact with applied patches"))
  1223                 root = self.series[start]
  1241                 root = self.series[start]
  1255                 end = start + 1
  1273                 end = start + 1
  1256             else:
  1274             else:
  1257                 end = self.series.index(patch, start) + 1
  1275                 end = self.series.index(patch, start) + 1
  1258 
  1276 
  1259             tobackup = set()
  1277             tobackup = set()
  1260             if not nobackup and force:
  1278             if (not nobackup and force) or check:
  1261                 m, a, r, d = self.checklocalchanges(repo, force=True)
  1279                 m, a, r, d = self.checklocalchanges(repo, force=True)
  1262                 tobackup.update(m + a)
  1280                 if check:
       
  1281                     tobackup.update(m + a + r + d)
       
  1282                 else:
       
  1283                     tobackup.update(m + a)
  1263 
  1284 
  1264             s = self.series[start:end]
  1285             s = self.series[start:end]
  1265             all_files = set()
  1286             all_files = set()
  1266             try:
  1287             try:
  1267                 if mergeq:
  1288                 if mergeq:
  1268                     ret = self.mergepatch(repo, mergeq, s, diffopts)
  1289                     ret = self.mergepatch(repo, mergeq, s, diffopts)
  1269                 else:
  1290                 else:
  1270                     ret = self.apply(repo, s, list, all_files=all_files,
  1291                     ret = self.apply(repo, s, list, all_files=all_files,
  1271                                      tobackup=tobackup)
  1292                                      tobackup=tobackup, check=check)
  1272             except:
  1293             except:
  1273                 self.ui.warn(_('cleaning up working directory...'))
  1294                 self.ui.warn(_('cleaning up working directory...'))
  1274                 node = repo.dirstate.p1()
  1295                 node = repo.dirstate.p1()
  1275                 hg.revert(repo, node, None)
  1296                 hg.revert(repo, node, None)
  1276                 # only remove unknown files that we know we touched or
  1297                 # only remove unknown files that we know we touched or
  1298         finally:
  1319         finally:
  1299             wlock.release()
  1320             wlock.release()
  1300 
  1321 
  1301     def pop(self, repo, patch=None, force=False, update=True, all=False,
  1322     def pop(self, repo, patch=None, force=False, update=True, all=False,
  1302             nobackup=False, check=False):
  1323             nobackup=False, check=False):
  1303         if force and check:
  1324         self.checkforcecheck(check, force)
  1304             raise util.Abort(_('cannot use both --force and --check'))
       
  1305         wlock = repo.wlock()
  1325         wlock = repo.wlock()
  1306         try:
  1326         try:
  1307             if patch:
  1327             if patch:
  1308                 # index, rev, patch
  1328                 # index, rev, patch
  1309                 info = self.isapplied(patch)
  1329                 info = self.isapplied(patch)
  2636         index = 0
  2656         index = 0
  2637     newpath = path + ".%d" % (index + 1)
  2657     newpath = path + ".%d" % (index + 1)
  2638     return newpath
  2658     return newpath
  2639 
  2659 
  2640 @command("^qpush",
  2660 @command("^qpush",
  2641          [('f', 'force', None, _('apply on top of local changes')),
  2661          [('c', 'check', None, _('tolerate non-conflicting local changes')),
       
  2662           ('f', 'force', None, _('apply on top of local changes')),
  2642           ('e', 'exact', None, _('apply the target patch to its recorded parent')),
  2663           ('e', 'exact', None, _('apply the target patch to its recorded parent')),
  2643           ('l', 'list', None, _('list patch name in commit text')),
  2664           ('l', 'list', None, _('list patch name in commit text')),
  2644           ('a', 'all', None, _('apply all patches')),
  2665           ('a', 'all', None, _('apply all patches')),
  2645           ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
  2666           ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
  2646           ('n', 'name', '',
  2667           ('n', 'name', '',
  2650           ('', 'no-backup', None, _('do not save backup copies of files'))],
  2671           ('', 'no-backup', None, _('do not save backup copies of files'))],
  2651          _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
  2672          _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
  2652 def push(ui, repo, patch=None, **opts):
  2673 def push(ui, repo, patch=None, **opts):
  2653     """push the next patch onto the stack
  2674     """push the next patch onto the stack
  2654 
  2675 
  2655     When -f/--force is applied, all local changes in patched files
  2676     By default, abort if the working directory contains uncommitted
  2656     will be lost.
  2677     changes. With -c/--check, abort only if the uncommitted files
       
  2678     overlap with patched files. With -f/--force, backup and patch over
       
  2679     uncommitted changes.
  2657 
  2680 
  2658     Return 0 on success.
  2681     Return 0 on success.
  2659     """
  2682     """
  2660     q = repo.mq
  2683     q = repo.mq
  2661     mergeq = None
  2684     mergeq = None
  2670             return 1
  2693             return 1
  2671         mergeq = queue(ui, repo.path, newpath)
  2694         mergeq = queue(ui, repo.path, newpath)
  2672         ui.warn(_("merging with queue at: %s\n") % mergeq.path)
  2695         ui.warn(_("merging with queue at: %s\n") % mergeq.path)
  2673     ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
  2696     ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
  2674                  mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
  2697                  mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
  2675                  exact=opts.get('exact'), nobackup=opts.get('no_backup'))
  2698                  exact=opts.get('exact'), nobackup=opts.get('no_backup'),
       
  2699                  check=opts.get('check'))
  2676     return ret
  2700     return ret
  2677 
  2701 
  2678 @command("^qpop",
  2702 @command("^qpop",
  2679          [('a', 'all', None, _('pop all patches')),
  2703          [('a', 'all', None, _('pop all patches')),
  2680           ('n', 'name', '',
  2704           ('n', 'name', '',