##// END OF EJS Templates
revert: move bulk of revert command from commands to cmdutil...
Angel Ezquerra -
r16304:a740fa28 default
parent child Browse files
Show More
@@ -1325,6 +1325,183 b' def commitforceeditor(repo, ctx, subs):'
1325
1325
1326 return text
1326 return text
1327
1327
1328 def revert(ui, repo, ctx, parents, *pats, **opts):
1329 parent, p2 = parents
1330 node = ctx.node()
1331
1332 mf = ctx.manifest()
1333 if node == parent:
1334 pmf = mf
1335 else:
1336 pmf = None
1337
1338 # need all matching names in dirstate and manifest of target rev,
1339 # so have to walk both. do not print errors if files exist in one
1340 # but not other.
1341
1342 names = {}
1343
1344 wlock = repo.wlock()
1345 try:
1346 # walk dirstate.
1347
1348 m = scmutil.match(repo[None], pats, opts)
1349 m.bad = lambda x, y: False
1350 for abs in repo.walk(m):
1351 names[abs] = m.rel(abs), m.exact(abs)
1352
1353 # walk target manifest.
1354
1355 def badfn(path, msg):
1356 if path in names:
1357 return
1358 if path in repo[node].substate:
1359 ui.warn("%s: %s\n" % (m.rel(path),
1360 'reverting subrepos is unsupported'))
1361 return
1362 path_ = path + '/'
1363 for f in names:
1364 if f.startswith(path_):
1365 return
1366 ui.warn("%s: %s\n" % (m.rel(path), msg))
1367
1368 m = scmutil.match(repo[node], pats, opts)
1369 m.bad = badfn
1370 for abs in repo[node].walk(m):
1371 if abs not in names:
1372 names[abs] = m.rel(abs), m.exact(abs)
1373
1374 m = scmutil.matchfiles(repo, names)
1375 changes = repo.status(match=m)[:4]
1376 modified, added, removed, deleted = map(set, changes)
1377
1378 # if f is a rename, also revert the source
1379 cwd = repo.getcwd()
1380 for f in added:
1381 src = repo.dirstate.copied(f)
1382 if src and src not in names and repo.dirstate[src] == 'r':
1383 removed.add(src)
1384 names[src] = (repo.pathto(src, cwd), True)
1385
1386 def removeforget(abs):
1387 if repo.dirstate[abs] == 'a':
1388 return _('forgetting %s\n')
1389 return _('removing %s\n')
1390
1391 revert = ([], _('reverting %s\n'))
1392 add = ([], _('adding %s\n'))
1393 remove = ([], removeforget)
1394 undelete = ([], _('undeleting %s\n'))
1395
1396 disptable = (
1397 # dispatch table:
1398 # file state
1399 # action if in target manifest
1400 # action if not in target manifest
1401 # make backup if in target manifest
1402 # make backup if not in target manifest
1403 (modified, revert, remove, True, True),
1404 (added, revert, remove, True, False),
1405 (removed, undelete, None, False, False),
1406 (deleted, revert, remove, False, False),
1407 )
1408
1409 for abs, (rel, exact) in sorted(names.items()):
1410 mfentry = mf.get(abs)
1411 target = repo.wjoin(abs)
1412 def handle(xlist, dobackup):
1413 xlist[0].append(abs)
1414 if (dobackup and not opts.get('no_backup') and
1415 os.path.lexists(target)):
1416 bakname = "%s.orig" % rel
1417 ui.note(_('saving current version of %s as %s\n') %
1418 (rel, bakname))
1419 if not opts.get('dry_run'):
1420 util.rename(target, bakname)
1421 if ui.verbose or not exact:
1422 msg = xlist[1]
1423 if not isinstance(msg, basestring):
1424 msg = msg(abs)
1425 ui.status(msg % rel)
1426 for table, hitlist, misslist, backuphit, backupmiss in disptable:
1427 if abs not in table:
1428 continue
1429 # file has changed in dirstate
1430 if mfentry:
1431 handle(hitlist, backuphit)
1432 elif misslist is not None:
1433 handle(misslist, backupmiss)
1434 break
1435 else:
1436 if abs not in repo.dirstate:
1437 if mfentry:
1438 handle(add, True)
1439 elif exact:
1440 ui.warn(_('file not managed: %s\n') % rel)
1441 continue
1442 # file has not changed in dirstate
1443 if node == parent:
1444 if exact:
1445 ui.warn(_('no changes needed to %s\n') % rel)
1446 continue
1447 if pmf is None:
1448 # only need parent manifest in this unlikely case,
1449 # so do not read by default
1450 pmf = repo[parent].manifest()
1451 if abs in pmf and mfentry:
1452 # if version of file is same in parent and target
1453 # manifests, do nothing
1454 if (pmf[abs] != mfentry or
1455 pmf.flags(abs) != mf.flags(abs)):
1456 handle(revert, False)
1457 else:
1458 handle(remove, False)
1459
1460 if not opts.get('dry_run'):
1461 def checkout(f):
1462 fc = ctx[f]
1463 repo.wwrite(f, fc.data(), fc.flags())
1464
1465 audit_path = scmutil.pathauditor(repo.root)
1466 for f in remove[0]:
1467 if repo.dirstate[f] == 'a':
1468 repo.dirstate.drop(f)
1469 continue
1470 audit_path(f)
1471 try:
1472 util.unlinkpath(repo.wjoin(f))
1473 except OSError:
1474 pass
1475 repo.dirstate.remove(f)
1476
1477 normal = None
1478 if node == parent:
1479 # We're reverting to our parent. If possible, we'd like status
1480 # to report the file as clean. We have to use normallookup for
1481 # merges to avoid losing information about merged/dirty files.
1482 if p2 != nullid:
1483 normal = repo.dirstate.normallookup
1484 else:
1485 normal = repo.dirstate.normal
1486 for f in revert[0]:
1487 checkout(f)
1488 if normal:
1489 normal(f)
1490
1491 for f in add[0]:
1492 checkout(f)
1493 repo.dirstate.add(f)
1494
1495 normal = repo.dirstate.normallookup
1496 if node == parent and p2 == nullid:
1497 normal = repo.dirstate.normal
1498 for f in undelete[0]:
1499 checkout(f)
1500 normal(f)
1501
1502 finally:
1503 wlock.release()
1504
1328 def command(table):
1505 def command(table):
1329 '''returns a function object bound to table which can be used as
1506 '''returns a function object bound to table which can be used as
1330 a decorator for populating table as a command table'''
1507 a decorator for populating table as a command table'''
@@ -4779,7 +4779,6 b' def revert(ui, repo, *pats, **opts):'
4779 hint=_('use "hg update" or see "hg help revert"'))
4779 hint=_('use "hg update" or see "hg help revert"'))
4780
4780
4781 ctx = scmutil.revsingle(repo, opts.get('rev'))
4781 ctx = scmutil.revsingle(repo, opts.get('rev'))
4782 node = ctx.node()
4783
4782
4784 if not pats and not opts.get('all'):
4783 if not pats and not opts.get('all'):
4785 msg = _("no files or directories specified")
4784 msg = _("no files or directories specified")
@@ -4788,6 +4787,7 b' def revert(ui, repo, *pats, **opts):'
4788 " or 'hg update -C .' to abort the merge")
4787 " or 'hg update -C .' to abort the merge")
4789 raise util.Abort(msg, hint=hint)
4788 raise util.Abort(msg, hint=hint)
4790 dirty = util.any(repo.status())
4789 dirty = util.any(repo.status())
4790 node = ctx.node()
4791 if node != parent:
4791 if node != parent:
4792 if dirty:
4792 if dirty:
4793 hint = _("uncommitted changes, use --all to discard all"
4793 hint = _("uncommitted changes, use --all to discard all"
@@ -4801,178 +4801,7 b' def revert(ui, repo, *pats, **opts):'
4801 hint = _("use --all to revert all files")
4801 hint = _("use --all to revert all files")
4802 raise util.Abort(msg, hint=hint)
4802 raise util.Abort(msg, hint=hint)
4803
4803
4804 mf = ctx.manifest()
4804 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4805 if node == parent:
4806 pmf = mf
4807 else:
4808 pmf = None
4809
4810 # need all matching names in dirstate and manifest of target rev,
4811 # so have to walk both. do not print errors if files exist in one
4812 # but not other.
4813
4814 names = {}
4815
4816 wlock = repo.wlock()
4817 try:
4818 # walk dirstate.
4819
4820 m = scmutil.match(repo[None], pats, opts)
4821 m.bad = lambda x, y: False
4822 for abs in repo.walk(m):
4823 names[abs] = m.rel(abs), m.exact(abs)
4824
4825 # walk target manifest.
4826
4827 def badfn(path, msg):
4828 if path in names:
4829 return
4830 if path in repo[node].substate:
4831 ui.warn("%s: %s\n" % (m.rel(path),
4832 'reverting subrepos is unsupported'))
4833 return
4834 path_ = path + '/'
4835 for f in names:
4836 if f.startswith(path_):
4837 return
4838 ui.warn("%s: %s\n" % (m.rel(path), msg))
4839
4840 m = scmutil.match(repo[node], pats, opts)
4841 m.bad = badfn
4842 for abs in repo[node].walk(m):
4843 if abs not in names:
4844 names[abs] = m.rel(abs), m.exact(abs)
4845
4846 m = scmutil.matchfiles(repo, names)
4847 changes = repo.status(match=m)[:4]
4848 modified, added, removed, deleted = map(set, changes)
4849
4850 # if f is a rename, also revert the source
4851 cwd = repo.getcwd()
4852 for f in added:
4853 src = repo.dirstate.copied(f)
4854 if src and src not in names and repo.dirstate[src] == 'r':
4855 removed.add(src)
4856 names[src] = (repo.pathto(src, cwd), True)
4857
4858 def removeforget(abs):
4859 if repo.dirstate[abs] == 'a':
4860 return _('forgetting %s\n')
4861 return _('removing %s\n')
4862
4863 revert = ([], _('reverting %s\n'))
4864 add = ([], _('adding %s\n'))
4865 remove = ([], removeforget)
4866 undelete = ([], _('undeleting %s\n'))
4867
4868 disptable = (
4869 # dispatch table:
4870 # file state
4871 # action if in target manifest
4872 # action if not in target manifest
4873 # make backup if in target manifest
4874 # make backup if not in target manifest
4875 (modified, revert, remove, True, True),
4876 (added, revert, remove, True, False),
4877 (removed, undelete, None, False, False),
4878 (deleted, revert, remove, False, False),
4879 )
4880
4881 for abs, (rel, exact) in sorted(names.items()):
4882 mfentry = mf.get(abs)
4883 target = repo.wjoin(abs)
4884 def handle(xlist, dobackup):
4885 xlist[0].append(abs)
4886 if (dobackup and not opts.get('no_backup') and
4887 os.path.lexists(target)):
4888 bakname = "%s.orig" % rel
4889 ui.note(_('saving current version of %s as %s\n') %
4890 (rel, bakname))
4891 if not opts.get('dry_run'):
4892 util.rename(target, bakname)
4893 if ui.verbose or not exact:
4894 msg = xlist[1]
4895 if not isinstance(msg, basestring):
4896 msg = msg(abs)
4897 ui.status(msg % rel)
4898 for table, hitlist, misslist, backuphit, backupmiss in disptable:
4899 if abs not in table:
4900 continue
4901 # file has changed in dirstate
4902 if mfentry:
4903 handle(hitlist, backuphit)
4904 elif misslist is not None:
4905 handle(misslist, backupmiss)
4906 break
4907 else:
4908 if abs not in repo.dirstate:
4909 if mfentry:
4910 handle(add, True)
4911 elif exact:
4912 ui.warn(_('file not managed: %s\n') % rel)
4913 continue
4914 # file has not changed in dirstate
4915 if node == parent:
4916 if exact:
4917 ui.warn(_('no changes needed to %s\n') % rel)
4918 continue
4919 if pmf is None:
4920 # only need parent manifest in this unlikely case,
4921 # so do not read by default
4922 pmf = repo[parent].manifest()
4923 if abs in pmf and mfentry:
4924 # if version of file is same in parent and target
4925 # manifests, do nothing
4926 if (pmf[abs] != mfentry or
4927 pmf.flags(abs) != mf.flags(abs)):
4928 handle(revert, False)
4929 else:
4930 handle(remove, False)
4931
4932 if not opts.get('dry_run'):
4933 def checkout(f):
4934 fc = ctx[f]
4935 repo.wwrite(f, fc.data(), fc.flags())
4936
4937 audit_path = scmutil.pathauditor(repo.root)
4938 for f in remove[0]:
4939 if repo.dirstate[f] == 'a':
4940 repo.dirstate.drop(f)
4941 continue
4942 audit_path(f)
4943 try:
4944 util.unlinkpath(repo.wjoin(f))
4945 except OSError:
4946 pass
4947 repo.dirstate.remove(f)
4948
4949 normal = None
4950 if node == parent:
4951 # We're reverting to our parent. If possible, we'd like status
4952 # to report the file as clean. We have to use normallookup for
4953 # merges to avoid losing information about merged/dirty files.
4954 if p2 != nullid:
4955 normal = repo.dirstate.normallookup
4956 else:
4957 normal = repo.dirstate.normal
4958 for f in revert[0]:
4959 checkout(f)
4960 if normal:
4961 normal(f)
4962
4963 for f in add[0]:
4964 checkout(f)
4965 repo.dirstate.add(f)
4966
4967 normal = repo.dirstate.normallookup
4968 if node == parent and p2 == nullid:
4969 normal = repo.dirstate.normal
4970 for f in undelete[0]:
4971 checkout(f)
4972 normal(f)
4973
4974 finally:
4975 wlock.release()
4976
4805
4977 @command('rollback', dryrunopts +
4806 @command('rollback', dryrunopts +
4978 [('f', 'force', False, _('ignore safety measures'))])
4807 [('f', 'force', False, _('ignore safety measures'))])
General Comments 0
You need to be logged in to leave comments. Login now