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