Mercurial > hg
changeset 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 | 6392bd7c26a8 |
files | mercurial/cmdutil.py mercurial/commands.py relnotes/next tests/test-rename-after-merge.t tests/test-rename-rev.t |
diffstat | 5 files changed, 130 insertions(+), 14 deletions(-) [+] |
line wrap: on
line diff
--- a/mercurial/cmdutil.py Thu Dec 26 14:02:50 2019 -0800 +++ b/mercurial/cmdutil.py Fri Dec 20 13:24:46 2019 -0800 @@ -1421,17 +1421,23 @@ forget = opts.get(b"forget") after = opts.get(b"after") dryrun = opts.get(b"dry_run") - ctx = repo[None] + rev = opts.get(b'at_rev') + if rev: + if not forget and not after: + # TODO: Remove this restriction and make it also create the copy + # targets (and remove the rename source if rename==True). + raise error.Abort(_(b'--at-rev requires --after')) + ctx = scmutil.revsingle(repo, rev) + if len(ctx.parents()) > 1: + raise error.Abort(_(b'cannot mark/unmark copy in merge commit')) + else: + ctx = repo[None] + pctx = ctx.p1() uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True) if forget: - rev = opts[b'at_rev'] - if rev: - ctx = scmutil.revsingle(repo, rev) - else: - ctx = repo[None] if ctx.rev() is None: new_ctx = ctx else: @@ -1484,9 +1490,6 @@ raise error.Abort(_(b'no destination specified')) dest = pats.pop() - if opts.get(b'at_rev'): - raise error.Abort(_("--at-rev is only supported with --forget")) - def walkpat(pat): srcs = [] m = scmutil.match(ctx, [pat], opts, globbed=True) @@ -1517,6 +1520,55 @@ srcs.append((abs, rel, exact)) return srcs + if ctx.rev() is not None: + rewriteutil.precheck(repo, [ctx.rev()], b'uncopy') + absdest = pathutil.canonpath(repo.root, cwd, dest) + if ctx.hasdir(absdest): + raise error.Abort( + _(b'%s: --at-rev does not support a directory as destination') + % uipathfn(absdest) + ) + if absdest not in ctx: + raise error.Abort( + _(b'%s: copy destination does not exist in %s') + % (uipathfn(absdest), ctx) + ) + + # avoid cycle context -> subrepo -> cmdutil + from . import context + + copylist = [] + for pat in pats: + srcs = walkpat(pat) + if not srcs: + continue + for abs, rel, exact in srcs: + copylist.append(abs) + + # TODO: Add support for `hg cp --at-rev . foo bar dir` and + # `hg cp --at-rev . dir1 dir2`, preferably unifying the code with the + # existing functions below. + if len(copylist) != 1: + raise error.Abort(_(b'--at-rev requires a single source')) + + new_ctx = context.overlayworkingctx(repo) + new_ctx.setbase(ctx.p1()) + mergemod.graft(repo, ctx, wctx=new_ctx) + + new_ctx.markcopied(absdest, copylist[0]) + + with repo.lock(): + mem_ctx = new_ctx.tomemctx_for_amend(ctx) + new_node = mem_ctx.commit() + + if repo.dirstate.p1() == ctx.node(): + with repo.dirstate.parentchange(): + scmutil.movedirstate(repo, repo[new_node]) + replacements = {ctx.node(): [new_node]} + scmutil.cleanupnodes(repo, replacements, b'copy', fixphase=True) + + return + # abssrc: hgsep # relsrc: ossep # otarget: ossep
--- a/mercurial/commands.py Thu Dec 26 14:02:50 2019 -0800 +++ b/mercurial/commands.py Fri Dec 20 13:24:46 2019 -0800 @@ -2315,7 +2315,7 @@ b'', b'at-rev', b'', - _(b'unmark copies in the given revision (EXPERIMENTAL)'), + _(b'(un)mark copies in the given revision (EXPERIMENTAL)'), _(b'REV'), ), ( @@ -2345,7 +2345,7 @@ all given (positional) arguments are unmarked as copies. The destination file(s) will be left in place (still tracked). - This command takes effect with the next commit. + This command takes effect with the next commit by default. Returns 0 on success, 1 if errors are encountered. """
--- a/relnotes/next Thu Dec 26 14:02:50 2019 -0800 +++ b/relnotes/next Fri Dec 20 13:24:46 2019 -0800 @@ -14,8 +14,12 @@ * `hg copy --forget` can be used to unmark a file as copied. +== New Experimental Features == -== New Experimental Features == + * `hg copy` now supports a `--at-rev` argument to mark files as + copied in the specified commit. It only works with `--after` for + now (i.e., it's only useful for marking files copied using non-hg + `cp` as copied). * Use `hg copy --forget --at-rev REV` to unmark already committed copies.
--- a/tests/test-rename-after-merge.t Thu Dec 26 14:02:50 2019 -0800 +++ b/tests/test-rename-after-merge.t Fri Dec 20 13:24:46 2019 -0800 @@ -120,10 +120,14 @@ $ hg log -r tip -C -v | grep copies copies: b2 (b1) -Test unmarking copies in merge commit +Test marking/unmarking copies in merge commit $ hg copy --forget --at-rev . b2 - abort: cannot unmark copy in merge commit + abort: cannot mark/unmark copy in merge commit + [255] + + $ hg copy --after --at-rev . b1 b2 + abort: cannot mark/unmark copy in merge commit [255] $ cd ..
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-rename-rev.t Fri Dec 20 13:24:46 2019 -0800 @@ -0,0 +1,56 @@ + $ hg init + $ mkdir d1 d1/d11 d2 + $ echo d1/a > d1/a + $ echo d1/ba > d1/ba + $ echo d1/a1 > d1/d11/a1 + $ echo d1/b > d1/b + $ echo d2/b > d2/b + $ hg add d1/a d1/b d1/ba d1/d11/a1 d2/b + $ hg commit -m "intial" + + +Test single file + +# One recoded copy, one copy to record after commit + $ hg cp d1/b d1/c + $ cp d1/b d1/d + $ hg add d1/d + $ hg ci -m 'copy d1/b to d1/c and d1/d' + $ hg st -C --change . + A d1/c + d1/b + A d1/d +# Errors out without --after for now + $ hg cp --at-rev . d1/b d1/d + abort: --at-rev requires --after + [255] +# Errors out with non-existent destination + $ hg cp -A --at-rev . d1/b d1/non-existent + abort: d1/non-existent: copy destination does not exist in 8a9d70fa20c9 + [255] +# Successful invocation + $ hg cp -A --at-rev . d1/b d1/d + saved backup bundle to $TESTTMP/.hg/strip-backup/8a9d70fa20c9-973ae357-copy.hg +# New copy is recorded, and previously recorded copy is also still there + $ hg st -C --change . + A d1/c + d1/b + A d1/d + d1/b + +Test using directory as destination + + $ hg co 0 + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ cp -R d1 d3 + $ hg add d3 + adding d3/a + adding d3/b + adding d3/ba + adding d3/d11/a1 + $ hg ci -m 'copy d1/ to d3/' + created new head + $ hg cp -A --at-rev . d1 d3 + abort: d3: --at-rev does not support a directory as destination + [255] +