copy: add option to unmark file as copied
authorMartin von Zweigbergk <martinvonz@google.com>
Fri, 20 Dec 2019 15:50:13 -0800
changeset 44364 8be0c63535b5
parent 44363 f7459da77f23
child 44365 7c4b98a4e536
copy: add option to unmark file as copied To unmark a file as copied, the user currently has to do this: hg forget <dest> hg add <dest> The new command simplifies that to: hg copy --forget <dest> That's not a very big improvement, but I'm planning to also teach `hg copy [--forget]` a `--at-rev` argument for marking/unmarking copies after commit (usually with `--at-rev .`). Differential Revision: https://phab.mercurial-scm.org/D8029
mercurial/cmdutil.py
mercurial/commands.py
relnotes/next
tests/test-completion.t
tests/test-copy.t
--- a/mercurial/cmdutil.py	Tue Feb 11 11:18:52 2020 +0100
+++ b/mercurial/cmdutil.py	Fri Dec 20 15:50:13 2019 -0800
@@ -1410,12 +1410,15 @@
 
 
 def copy(ui, repo, pats, opts, rename=False):
+    check_incompatible_arguments(opts, b'forget', [b'dry_run'])
+
     # called with the repo lock held
     #
     # hgsep => pathname that uses "/" to separate directories
     # ossep => pathname that uses os.sep to separate directories
     cwd = repo.getcwd()
     targets = {}
+    forget = opts.get(b"forget")
     after = opts.get(b"after")
     dryrun = opts.get(b"dry_run")
     ctx = repo[None]
@@ -1423,6 +1426,24 @@
 
     uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
 
+    if forget:
+        match = scmutil.match(wctx, pats, opts)
+
+        current_copies = wctx.p1copies()
+        current_copies.update(wctx.p2copies())
+
+        for f in wctx.walk(match):
+            if f in current_copies:
+                wctx[f].markcopied(None)
+            elif match.exact(f):
+                ui.warn(
+                    _(
+                        b'%s: not unmarking as copy - file is not marked as copied\n'
+                    )
+                    % uipathfn(f)
+                )
+        return
+
     def walkpat(pat):
         srcs = []
         m = scmutil.match(ctx, [pat], opts, globbed=True)
--- a/mercurial/commands.py	Tue Feb 11 11:18:52 2020 +0100
+++ b/mercurial/commands.py	Fri Dec 20 15:50:13 2019 -0800
@@ -2309,6 +2309,7 @@
 @command(
     b'copy|cp',
     [
+        (b'', b'forget', None, _(b'unmark a file as copied')),
         (b'A', b'after', None, _(b'record a copy that has already occurred')),
         (
             b'f',
@@ -2333,8 +2334,11 @@
     exist in the working directory. If invoked with -A/--after, the
     operation is recorded, but no copying is performed.
 
-    This command takes effect with the next commit. To undo a copy
-    before that, see :hg:`revert`.
+    To undo marking a file as copied, use --forget. With that option,
+    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.
 
     Returns 0 on success, 1 if errors are encountered.
     """
--- a/relnotes/next	Tue Feb 11 11:18:52 2020 +0100
+++ b/relnotes/next	Fri Dec 20 15:50:13 2019 -0800
@@ -12,6 +12,8 @@
    commits that are being merged, when there are conflicts. Also works
    for conflicts caused by e.g. `hg graft`.
 
+ * `hg copy --forget` can be used to unmark a file as copied.
+
 
 == New Experimental Features ==
 
--- a/tests/test-completion.t	Tue Feb 11 11:18:52 2020 +0100
+++ b/tests/test-completion.t	Fri Dec 20 15:50:13 2019 -0800
@@ -257,7 +257,7 @@
   commit: addremove, close-branch, amend, secret, edit, force-close-branch, interactive, include, exclude, message, logfile, date, user, subrepos
   config: untrusted, edit, local, global, template
   continue: dry-run
-  copy: after, force, include, exclude, dry-run
+  copy: forget, after, force, include, exclude, dry-run
   debugancestor: 
   debugapplystreamclonebundle: 
   debugbuilddag: mergeable-file, overwritten-file, new-file
--- a/tests/test-copy.t	Tue Feb 11 11:18:52 2020 +0100
+++ b/tests/test-copy.t	Fri Dec 20 15:50:13 2019 -0800
@@ -262,5 +262,62 @@
   xyzzy: not overwriting - file exists
   ('hg copy --after' to record the copy)
   [1]
+  $ hg co -qC .
+  $ rm baz xyzzy
+
+
+Test unmarking copy of a single file
+
+# Set up by creating a copy
+  $ hg cp bar baz
+# Test uncopying a non-existent file
+  $ hg copy --forget non-existent
+  non-existent: $ENOENT$
+# Test uncopying an tracked but unrelated file
+  $ hg copy --forget foo
+  foo: not unmarking as copy - file is not marked as copied
+# Test uncopying a copy source
+  $ hg copy --forget bar
+  bar: not unmarking as copy - file is not marked as copied
+# baz should still be marked as a copy
+  $ hg st -C
+  A baz
+    bar
+# Test the normal case
+  $ hg copy --forget baz
+  $ hg st -C
+  A baz
+# Test uncopy with matching an non-matching patterns
+  $ hg cp bar baz --after
+  $ hg copy --forget bar baz
+  bar: not unmarking as copy - file is not marked as copied
+  $ hg st -C
+  A baz
+# Test uncopy with no exact matches
+  $ hg cp bar baz --after
+  $ hg copy --forget .
+  $ hg st -C
+  A baz
+  $ hg forget baz
+  $ rm baz
+
+Test unmarking copy of a directory
+
+  $ mkdir dir
+  $ echo foo > dir/foo
+  $ echo bar > dir/bar
+  $ hg add dir
+  adding dir/bar
+  adding dir/foo
+  $ hg ci -m 'add dir/'
+  $ hg cp dir dir2
+  copying dir/bar to dir2/bar
+  copying dir/foo to dir2/foo
+  $ touch dir2/untracked
+  $ hg copy --forget dir2
+  $ hg st -C
+  A dir2/bar
+  A dir2/foo
+  ? dir2/untracked
 
   $ cd ..