Mercurial > hg
comparison mercurial/cmdutil.py @ 44367:9dab3fa64325
copy: add experimental support for marking committed copies
The simplest way I'm aware of to mark a file as copied/moved after
committing is this:
hg uncommit --keep <src> <dest> # <src> needed for move, but not copy
hg mv --after <src> <dest>
hg amend
This patch teaches `hg copy` a `--at-rev` argument to simplify that
into:
hg copy --after --at-rev . <src> <dest>
In addition to being simpler, it doesn't touch the working copy, so it
can easily be used even if the destination file has been modified in
the working copy.
Differential Revision: https://phab.mercurial-scm.org/D8035
author | Martin von Zweigbergk <martinvonz@google.com> |
---|---|
date | Fri, 20 Dec 2019 13:24:46 -0800 |
parents | d8b49bf6cfec |
children | f5c006621f07 |
comparison
equal
deleted
inserted
replaced
44366:d8b49bf6cfec | 44367:9dab3fa64325 |
---|---|
1419 cwd = repo.getcwd() | 1419 cwd = repo.getcwd() |
1420 targets = {} | 1420 targets = {} |
1421 forget = opts.get(b"forget") | 1421 forget = opts.get(b"forget") |
1422 after = opts.get(b"after") | 1422 after = opts.get(b"after") |
1423 dryrun = opts.get(b"dry_run") | 1423 dryrun = opts.get(b"dry_run") |
1424 ctx = repo[None] | 1424 rev = opts.get(b'at_rev') |
1425 if rev: | |
1426 if not forget and not after: | |
1427 # TODO: Remove this restriction and make it also create the copy | |
1428 # targets (and remove the rename source if rename==True). | |
1429 raise error.Abort(_(b'--at-rev requires --after')) | |
1430 ctx = scmutil.revsingle(repo, rev) | |
1431 if len(ctx.parents()) > 1: | |
1432 raise error.Abort(_(b'cannot mark/unmark copy in merge commit')) | |
1433 else: | |
1434 ctx = repo[None] | |
1435 | |
1425 pctx = ctx.p1() | 1436 pctx = ctx.p1() |
1426 | 1437 |
1427 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True) | 1438 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True) |
1428 | 1439 |
1429 if forget: | 1440 if forget: |
1430 rev = opts[b'at_rev'] | |
1431 if rev: | |
1432 ctx = scmutil.revsingle(repo, rev) | |
1433 else: | |
1434 ctx = repo[None] | |
1435 if ctx.rev() is None: | 1441 if ctx.rev() is None: |
1436 new_ctx = ctx | 1442 new_ctx = ctx |
1437 else: | 1443 else: |
1438 if len(ctx.parents()) > 1: | 1444 if len(ctx.parents()) > 1: |
1439 raise error.Abort(_(b'cannot unmark copy in merge commit')) | 1445 raise error.Abort(_(b'cannot unmark copy in merge commit')) |
1481 if not pats: | 1487 if not pats: |
1482 raise error.Abort(_(b'no source or destination specified')) | 1488 raise error.Abort(_(b'no source or destination specified')) |
1483 if len(pats) == 1: | 1489 if len(pats) == 1: |
1484 raise error.Abort(_(b'no destination specified')) | 1490 raise error.Abort(_(b'no destination specified')) |
1485 dest = pats.pop() | 1491 dest = pats.pop() |
1486 | |
1487 if opts.get(b'at_rev'): | |
1488 raise error.Abort(_("--at-rev is only supported with --forget")) | |
1489 | 1492 |
1490 def walkpat(pat): | 1493 def walkpat(pat): |
1491 srcs = [] | 1494 srcs = [] |
1492 m = scmutil.match(ctx, [pat], opts, globbed=True) | 1495 m = scmutil.match(ctx, [pat], opts, globbed=True) |
1493 for abs in ctx.walk(m): | 1496 for abs in ctx.walk(m): |
1514 | 1517 |
1515 # abs: hgsep | 1518 # abs: hgsep |
1516 # rel: ossep | 1519 # rel: ossep |
1517 srcs.append((abs, rel, exact)) | 1520 srcs.append((abs, rel, exact)) |
1518 return srcs | 1521 return srcs |
1522 | |
1523 if ctx.rev() is not None: | |
1524 rewriteutil.precheck(repo, [ctx.rev()], b'uncopy') | |
1525 absdest = pathutil.canonpath(repo.root, cwd, dest) | |
1526 if ctx.hasdir(absdest): | |
1527 raise error.Abort( | |
1528 _(b'%s: --at-rev does not support a directory as destination') | |
1529 % uipathfn(absdest) | |
1530 ) | |
1531 if absdest not in ctx: | |
1532 raise error.Abort( | |
1533 _(b'%s: copy destination does not exist in %s') | |
1534 % (uipathfn(absdest), ctx) | |
1535 ) | |
1536 | |
1537 # avoid cycle context -> subrepo -> cmdutil | |
1538 from . import context | |
1539 | |
1540 copylist = [] | |
1541 for pat in pats: | |
1542 srcs = walkpat(pat) | |
1543 if not srcs: | |
1544 continue | |
1545 for abs, rel, exact in srcs: | |
1546 copylist.append(abs) | |
1547 | |
1548 # TODO: Add support for `hg cp --at-rev . foo bar dir` and | |
1549 # `hg cp --at-rev . dir1 dir2`, preferably unifying the code with the | |
1550 # existing functions below. | |
1551 if len(copylist) != 1: | |
1552 raise error.Abort(_(b'--at-rev requires a single source')) | |
1553 | |
1554 new_ctx = context.overlayworkingctx(repo) | |
1555 new_ctx.setbase(ctx.p1()) | |
1556 mergemod.graft(repo, ctx, wctx=new_ctx) | |
1557 | |
1558 new_ctx.markcopied(absdest, copylist[0]) | |
1559 | |
1560 with repo.lock(): | |
1561 mem_ctx = new_ctx.tomemctx_for_amend(ctx) | |
1562 new_node = mem_ctx.commit() | |
1563 | |
1564 if repo.dirstate.p1() == ctx.node(): | |
1565 with repo.dirstate.parentchange(): | |
1566 scmutil.movedirstate(repo, repo[new_node]) | |
1567 replacements = {ctx.node(): [new_node]} | |
1568 scmutil.cleanupnodes(repo, replacements, b'copy', fixphase=True) | |
1569 | |
1570 return | |
1519 | 1571 |
1520 # abssrc: hgsep | 1572 # abssrc: hgsep |
1521 # relsrc: ossep | 1573 # relsrc: ossep |
1522 # otarget: ossep | 1574 # otarget: ossep |
1523 def copyfile(abssrc, relsrc, otarget, exact): | 1575 def copyfile(abssrc, relsrc, otarget, exact): |