Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/cmdutil.py @ 16304:a740fa28d718
revert: move bulk of revert command from commands to cmdutil
This revision has no functionality change. The code on the original
commands.revert() function has been split. The first part of the
original code, which checks that the command inputs are correct
remains in commands.revert(). The rest of the function, which performs
the actual revert operation has been moved into cmdutil.revert().
The purpose of this change is to make it easier to perform a revert
operation, from other parts of the code. This may be used to implement
reverting of subrepos.
author | Angel Ezquerra <angel.ezquerra@gmail.com> |
---|---|
date | Wed, 28 Mar 2012 11:42:17 +0200 |
parents | 6c4dbe28dda3 |
children | 84ba30e8c790 |
comparison
equal
deleted
inserted
replaced
16295:ba42eb722bb3 | 16304:a740fa28d718 |
---|---|
1323 if not text.strip(): | 1323 if not text.strip(): |
1324 raise util.Abort(_("empty commit message")) | 1324 raise util.Abort(_("empty commit message")) |
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''' |
1331 | 1508 |
1332 def cmd(name, options, synopsis=None): | 1509 def cmd(name, options, synopsis=None): |