Mercurial > hg
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', '', |