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):