comparison hgext/mq.py @ 16654:490ed8972f1b

mq: introduce qpush --check qpush --check let you qpush with uncommitted files not overlapping patched files.
author Patrick Mezard <patrick@mezard.eu>
date Sat, 12 May 2012 00:19:30 +0200
parents 73b8c2554be8
children 6ca125af882f
comparison
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', '',