Patchwork D8029: uncopy: add new `hg uncopy` command

login
register
mail settings
Submitter phabricator
Date Jan. 28, 2020, 11:54 p.m.
Message ID <differential-rev-PHID-DREV-rsrbi4kxiym52ljeyuh3-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/44721/
State Superseded
Headers show

Comments

phabricator - Jan. 28, 2020, 11:54 p.m.
martinvonz created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  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 uncopy <dest>
  
  That's not a very big improvement, but I'm planning to also teach both
  `hg copy` and `hg uncopy` a `-r` argument for marking/unmarking copies
  after commit (usually with `-r .`).

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D8029

AFFECTED FILES
  mercurial/cmdutil.py
  mercurial/commands.py
  relnotes/5.3
  relnotes/next
  tests/test-completion.t
  tests/test-copy.t
  tests/test-globalopts.t
  tests/test-help-hide.t
  tests/test-help.t
  tests/test-hgweb-json.t

CHANGE DETAILS




To: martinvonz, #hg-reviewers
Cc: mercurial-devel
phabricator - Jan. 29, 2020, 5 p.m.
marmoute added a comment.


  Coudl we use a flag for to `hg copy` for that ? something like `hg copy --forget`

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D8029/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D8029

To: martinvonz, #hg-reviewers
Cc: marmoute, mercurial-devel
phabricator - Jan. 29, 2020, 5:06 p.m.
martinvonz added a comment.


  In D8029#118463 <https://phab.mercurial-scm.org/D8029#118463>, @marmoute wrote:
  
  > Coudl we use a flag for to `hg copy` for that ? something like `hg copy --forget`
  
  Why would you prefer that? Discoverability?
  
  An argument against it is that the commands take different flags and arguments (for example, `hg uncopy` takes only the destination, no source).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D8029/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D8029

To: martinvonz, #hg-reviewers
Cc: marmoute, mercurial-devel
phabricator - Jan. 29, 2020, 10:42 p.m.
durin42 added a comment.


  In D8029#118464 <https://phab.mercurial-scm.org/D8029#118464>, @martinvonz wrote:
  
  > In D8029#118463 <https://phab.mercurial-scm.org/D8029#118463>, @marmoute wrote:
  >
  >> Coudl we use a flag for to `hg copy` for that ? something like `hg copy --forget`
  >
  > Why would you prefer that? Discoverability?
  > An argument against it is that the commands take different flags and arguments (for example, `hg uncopy` takes only the destination, no source).
  
  I'm convinced enough that `hg uncopy` is enough clearer to justify being its own thing.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D8029/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D8029

To: martinvonz, #hg-reviewers
Cc: durin42, marmoute, mercurial-devel
phabricator - Jan. 30, 2020, 2:19 p.m.
pulkit added inline comments.

INLINE COMMENTS

> cmdutil.py:1698
> +def uncopy(ui, repo, pats, opts):
> +    ctx = repo[None]
> +

generally we refer to `repo[None]` as `wctx` right?

> commands.py:7502
> +)
> +def uncopy(ui, repo, *pats, **opts):
> +    """unmark files as copied"""

We should add descriptive function documentation which will be visible to user when they do `hg uncopy -h`.

REPOSITORY
  rHG Mercurial

BRANCH
  default

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D8029/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D8029

To: martinvonz, #hg-reviewers, durin42
Cc: pulkit, durin42, marmoute, mercurial-devel
phabricator - Jan. 30, 2020, 2:40 p.m.
martinvonz added inline comments.

INLINE COMMENTS

> pulkit wrote in cmdutil.py:1698
> generally we refer to `repo[None]` as `wctx` right?

I didn't do that because I had planned the next patch, where the same code is used for working copy and not, so `ctx` isn't necessarily the working copy.

> pulkit wrote in commands.py:7502
> We should add descriptive function documentation which will be visible to user when they do `hg uncopy -h`.

Oops, I just forgot about that, I think. I'll send a follow-up (I assume you know that many of the patches you're reviewing now are already queued).

REPOSITORY
  rHG Mercurial

BRANCH
  default

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D8029/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D8029

To: martinvonz, #hg-reviewers, durin42
Cc: pulkit, durin42, marmoute, mercurial-devel
phabricator - Jan. 30, 2020, 5:08 p.m.
martinvonz added inline comments.

INLINE COMMENTS

> martinvonz wrote in commands.py:7502
> Oops, I just forgot about that, I think. I'll send a follow-up (I assume you know that many of the patches you're reviewing now are already queued).

Sorry, I forgot that this series was just accepted but not queued :) I've added documentation now.

REPOSITORY
  rHG Mercurial

BRANCH
  default

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D8029/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D8029

To: martinvonz, #hg-reviewers, durin42
Cc: pulkit, durin42, marmoute, mercurial-devel
phabricator - Feb. 7, 2020, 8:29 p.m.
This revision now requires changes to proceed.
marmoute added a comment.
marmoute requested changes to this revision.


  I am still not convinced by the command name.
  
  In D8029#118464 <https://phab.mercurial-scm.org/D8029#118464>, @martinvonz wrote:
  
  > In D8029#118463 <https://phab.mercurial-scm.org/D8029#118463>, @marmoute wrote:
  >
  >> Coudl we use a flag for to `hg copy` for that ? something like `hg copy --forget`
  >
  > Why would you prefer that? Discoverability?
  
  Discoverability, avoiding command creep, interface clarify, avoiding interface of similar command drifting appart from each other. The two command really deal with the same data in the same way. Having them one would
  
  > An argument against it is that the commands take different flags and arguments (for example, `hg uncopy` takes only the destination, no source).
  
  I don't think it is a big deal `hg copy source dest`, `hg copy --forget dest` seems clear enough to me.
  
  One of the thing that bother me is that `hg uncopy` is not reverse of `hg copy`, it is the reverse `hg copy --after` I expect it can be a source of confusion. using `--after` better map the behavior of the function, that is closer to `hg forget` as you point out in your changeset description.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D8029/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D8029

To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: pulkit, durin42, marmoute, mercurial-devel
phabricator - Feb. 7, 2020, 8:35 p.m.
martinvonz added a comment.


  In D8029#119813 <https://phab.mercurial-scm.org/D8029#119813>, @marmoute wrote:
  
  > I am still not convinced by the command name.
  >
  > In D8029#118464 <https://phab.mercurial-scm.org/D8029#118464>, @martinvonz wrote:
  >
  >> In D8029#118463 <https://phab.mercurial-scm.org/D8029#118463>, @marmoute wrote:
  >>
  >>> Coudl we use a flag for to `hg copy` for that ? something like `hg copy --forget`
  >>
  >> Why would you prefer that? Discoverability?
  >
  > Discoverability, avoiding command creep, interface clarify, avoiding interface of similar command drifting appart from each other. The two command really deal with the same data in the same way. Having them one would
  >
  >> An argument against it is that the commands take different flags and arguments (for example, `hg uncopy` takes only the destination, no source).
  >
  > I don't think it is a big deal `hg copy source dest`, `hg copy --forget dest` seems clear enough to me.
  > One of the thing that bother me is that `hg uncopy` is not reverse of `hg copy`, it is the reverse `hg copy --after` I expect it can be a source of confusion. using `--after` better map the behavior of the function, that is closer to `hg forget` as you point out in your changeset description.
  
  You're saying you want the user to say `hg copy --forget --after dest` to unmark it as copied?

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D8029/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D8029

To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: pulkit, durin42, marmoute, mercurial-devel
phabricator - Feb. 10, 2020, 10:49 p.m.
martinvonz added a comment.


  In D8029#119818 <https://phab.mercurial-scm.org/D8029#119818>, @martinvonz wrote:
  
  > In D8029#119813 <https://phab.mercurial-scm.org/D8029#119813>, @marmoute wrote:
  >
  >> I am still not convinced by the command name.
  >>
  >> In D8029#118464 <https://phab.mercurial-scm.org/D8029#118464>, @martinvonz wrote:
  >>
  >>> In D8029#118463 <https://phab.mercurial-scm.org/D8029#118463>, @marmoute wrote:
  >>>
  >>>> Coudl we use a flag for to `hg copy` for that ? something like `hg copy --forget`
  >>>
  >>> Why would you prefer that? Discoverability?
  >>
  >> Discoverability, avoiding command creep, interface clarify, avoiding interface of similar command drifting appart from each other. The two command really deal with the same data in the same way. Having them one would
  >>
  >>> An argument against it is that the commands take different flags and arguments (for example, `hg uncopy` takes only the destination, no source).
  >>
  >> I don't think it is a big deal `hg copy source dest`, `hg copy --forget dest` seems clear enough to me.
  >> One of the thing that bother me is that `hg uncopy` is not reverse of `hg copy`, it is the reverse `hg copy --after` I expect it can be a source of confusion. using `--after` better map the behavior of the function, that is closer to `hg forget` as you point out in your changeset description.
  >
  > You're saying you want the user to say `hg copy --forget --after dest` to unmark it as copied?
  
  Since both you and @durin42 wanted `hg copy --forget`, I've updated the series to do it that way. I decided not to require `hg --after` since `--forget` could be interpreted to mean both "forget the copy" and "forget the file" (i.e. untrack but don't remove) at the same time. That's convenient :)
  
  Note that the synopsis of the command is now not quite correct because `hg copy --forget` doesn't have `SOURCE... DEST` but `DEST...`. timeless suggested adding multiple synopsis lines à la `man cp`, but our help system decided not to respect my newline. So I just gave up on it. I described it in the help text instead.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D8029/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D8029

To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: pulkit, durin42, marmoute, mercurial-devel
phabricator - Feb. 12, 2020, 7:41 p.m.
durin42 added a comment.
durin42 accepted this revision.


  LGTM, would like @marmoute to chime in.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D8029/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D8029

To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: pulkit, durin42, marmoute, mercurial-devel
phabricator - Feb. 13, 2020, 6:58 p.m.
pulkit added a comment.


  The commit message has a mention of `uncopy` which needs to be removed.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D8029/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D8029

To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: pulkit, durin42, marmoute, mercurial-devel
phabricator - Feb. 13, 2020, 7:10 p.m.
martinvonz added a comment.


  In D8029#120701 <https://phab.mercurial-scm.org/D8029#120701>, @pulkit wrote:
  
  > The commit message has a mention of `uncopy` which needs to be removed.
  
  Good catch. Done.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D8029/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D8029

To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: pulkit, durin42, marmoute, mercurial-devel
phabricator - Feb. 13, 2020, 11:07 p.m.
martinvonz added inline comments.

INLINE COMMENTS

> marmoute wrote in cmdutil.py:1413
> Why are these two incompatible ? I can imagine running `--forget` with `--dryrun`

Just not implemented yet. Are you asking just out of curiosity or do you think it needs to be clarified to the user?

> marmoute wrote in commands.py:2339
> I find this text a bit confusing. Do you mean the the synopsys is `hg copy --forget FILE` ?
> 
> If so, what about something like:
> 
>   To undo marking a file as copied, use --forget. In that case, any copy informations attached to the command argument will be forgotten: `hg copy --forget FILE+`. The target file(s) will be left in place (still tracked).

If you first do `hg cp a b`, you can't then do `hg cp --forget a`, it needs to be the destination. The "SOURCE" and "DEST" names refer to the names in the synopsis. I was trying to clarify that that they're treated the same, but maybe there will be no confusion and I should not mention it?

> marmoute wrote in test-copy.t:304
> This block will be a great place to add a `--dry-run` test ;-).

I'll leave that for a follow-up. I don't think it should be a requirement.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D8029/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D8029

To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: pulkit, durin42, marmoute, mercurial-devel
phabricator - Feb. 13, 2020, 11:14 p.m.
marmoute added inline comments.

INLINE COMMENTS

> martinvonz wrote in commands.py:2339
> If you first do `hg cp a b`, you can't then do `hg cp --forget a`, it needs to be the destination. The "SOURCE" and "DEST" names refer to the names in the synopsis. I was trying to clarify that that they're treated the same, but maybe there will be no confusion and I should not mention it?

doing `hg cp a b` does not add copies information to `a`, so I don't think there would be confusion from the users.

What I find the most confusing part is the piece refering to `SOURCE` and `DEST` for an operation that as no such pair. I would refer to an explicite `TARGET` argument and include some kind of synopsys (at that location in the documentation)

> martinvonz wrote in test-copy.t:304
> I'll leave that for a follow-up. I don't think it should be a requirement.

Okay that seems fair. Do you think you could impletement that before 5.4 ?

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D8029/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D8029

To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: pulkit, durin42, marmoute, mercurial-devel
phabricator - Feb. 13, 2020, 11:20 p.m.
martinvonz added inline comments.

INLINE COMMENTS

> marmoute wrote in commands.py:2339
> doing `hg cp a b` does not add copies information to `a`, so I don't think there would be confusion from the users.
> 
> What I find the most confusing part is the piece refering to `SOURCE` and `DEST` for an operation that as no such pair. I would refer to an explicite `TARGET` argument and include some kind of synopsys (at that location in the documentation)

> What I find the most confusing part is the piece refering to SOURCE and DEST for an 
> operation that as no such pair. I would refer to an explicite TARGET argument and
> include some kind of synopsys (at that location in the documentation)

I tried to add a separate synopsis line for `--forget`, as I said in https://phab.mercurial-scm.org/D8029#120191.

I'll still update the comment here to not refer to SOURCE and DEST since it's clearly confusing you, but I don't want to refer to TARGET or FILE either since that's not defined anywhere.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D8029/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D8029

To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: pulkit, durin42, marmoute, mercurial-devel
phabricator - Feb. 13, 2020, 11:49 p.m.
marmoute added inline comments.

INLINE COMMENTS

> martinvonz wrote in commands.py:2339
> > What I find the most confusing part is the piece refering to SOURCE and DEST for an 
> > operation that as no such pair. I would refer to an explicite TARGET argument and
> > include some kind of synopsys (at that location in the documentation)
> 
> I tried to add a separate synopsis line for `--forget`, as I said in https://phab.mercurial-scm.org/D8029#120191.
> 
> I'll still update the comment here to not refer to SOURCE and DEST since it's clearly confusing you, but I don't want to refer to TARGET or FILE either since that's not defined anywhere.

What i mean is to put the equivalent of a synopsys line inside the documentation text at tle location where --forget is defined.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D8029/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D8029

To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: pulkit, durin42, marmoute, mercurial-devel
phabricator - Feb. 13, 2020, 11:51 p.m.
martinvonz added inline comments.

INLINE COMMENTS

> marmoute wrote in commands.py:2339
> What i mean is to put the equivalent of a synopsys line inside the documentation text at tle location where --forget is defined.

Hmm, do we do that elsewhere? It would seem out of place to me, but if there's precedent for doing it, I can do it here too.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D8029/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D8029

To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: pulkit, durin42, marmoute, mercurial-devel
phabricator - Feb. 13, 2020, 11:59 p.m.
marmoute added inline comments.

INLINE COMMENTS

> martinvonz wrote in commands.py:2339
> Hmm, do we do that elsewhere? It would seem out of place to me, but if there's precedent for doing it, I can do it here too.

The other command with variable number of arguments I can think of usually mark them as optional in their synopsis (eg: `hg bookmarks`), but I feel like the situation in a bit different.

The new doc is clearer, but still not very clear. Can you try adding the synopsis to see how it looks like?

An alternative is to augment the help test to support multiple synopsys, "it can't be that hard".

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D8029/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D8029

To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: pulkit, durin42, marmoute, mercurial-devel
phabricator - Feb. 14, 2020, 12:03 a.m.
martinvonz added inline comments.

INLINE COMMENTS

> marmoute wrote in commands.py:2339
> The other command with variable number of arguments I can think of usually mark them as optional in their synopsis (eg: `hg bookmarks`), but I feel like the situation in a bit different.
> 
> The new doc is clearer, but still not very clear. Can you try adding the synopsis to see how it looks like?
> 
> An alternative is to augment the help test to support multiple synopsys, "it can't be that hard".

I'll let other reviewers decide if it's worth blocking the patch or series for that. I think it's quite clear already.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D8029/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D8029

To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: pulkit, durin42, marmoute, mercurial-devel

Patch

diff --git a/tests/test-hgweb-json.t b/tests/test-hgweb-json.t
--- a/tests/test-hgweb-json.t
+++ b/tests/test-hgweb-json.t
@@ -2226,6 +2226,10 @@ 
         "topic": "unbundle"
       },
       {
+        "summary": "unmark files as copied",
+        "topic": "uncopy"
+      },
+      {
         "summary": "restore a shelved change to the working directory",
         "topic": "unshelve"
       },
diff --git a/tests/test-help.t b/tests/test-help.t
--- a/tests/test-help.t
+++ b/tests/test-help.t
@@ -93,6 +93,7 @@ 
    copy          mark files as copied for the next commit
    diff          diff repository (or selected files)
    grep          search for a pattern in specified files
+   uncopy        unmark files as copied
   
   Change navigation:
   
@@ -221,6 +222,7 @@ 
    copy          mark files as copied for the next commit
    diff          diff repository (or selected files)
    grep          search for a pattern in specified files
+   uncopy        unmark files as copied
   
   Change navigation:
   
@@ -2754,6 +2756,13 @@ 
   apply one or more bundle files
   </td></tr>
   <tr><td>
+  <a href="/help/uncopy">
+  uncopy
+  </a>
+  </td><td>
+  unmark files as copied
+  </td></tr>
+  <tr><td>
   <a href="/help/unshelve">
   unshelve
   </a>
diff --git a/tests/test-help-hide.t b/tests/test-help-hide.t
--- a/tests/test-help-hide.t
+++ b/tests/test-help-hide.t
@@ -41,6 +41,7 @@ 
    copy          mark files as copied for the next commit
    diff          diff repository (or selected files)
    grep          search for a pattern in specified files
+   uncopy        unmark files as copied
   
   Change navigation:
   
@@ -177,6 +178,7 @@ 
    copy          mark files as copied for the next commit
    diff          diff repository (or selected files)
    grep          search for a pattern in specified files
+   uncopy        unmark files as copied
   
   Change navigation:
   
diff --git a/tests/test-globalopts.t b/tests/test-globalopts.t
--- a/tests/test-globalopts.t
+++ b/tests/test-globalopts.t
@@ -337,6 +337,7 @@ 
    copy          mark files as copied for the next commit
    diff          diff repository (or selected files)
    grep          search for a pattern in specified files
+   uncopy        unmark files as copied
   
   Change navigation:
   
@@ -469,6 +470,7 @@ 
    copy          mark files as copied for the next commit
    diff          diff repository (or selected files)
    grep          search for a pattern in specified files
+   uncopy        unmark files as copied
   
   Change navigation:
   
diff --git a/tests/test-copy.t b/tests/test-copy.t
--- a/tests/test-copy.t
+++ b/tests/test-copy.t
@@ -262,5 +262,62 @@ 
   xyzzy: not overwriting - file exists
   ('hg copy --after' to record the copy)
   [1]
+  $ hg co -qC .
+  $ rm baz xyzzy
+
+
+Test uncopy of a single file
+
+# Set up by creating a copy
+  $ hg cp bar baz
+# Test uncopying a non-existent file
+  $ hg uncopy non-existent
+  non-existent: $ENOENT$
+# Test uncopying an tracked but unrelated file
+  $ hg uncopy foo
+  foo: not uncopying - file is not marked as copied
+# Test uncopying a copy source
+  $ hg uncopy bar
+  bar: not uncopying - 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 uncopy baz
+  $ hg st -C
+  A baz
+# Test uncopy with matching an non-matching patterns
+  $ hg cp bar baz --after
+  $ hg uncopy bar baz
+  bar: not uncopying - file is not marked as copied
+  $ hg st -C
+  A baz
+# Test uncopy with no exact matches
+  $ hg cp bar baz --after
+  $ hg uncopy .
+  $ hg st -C
+  A baz
+  $ hg forget baz
+  $ rm baz
+
+Test uncopy 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 uncopy dir2
+  $ hg st -C
+  A dir2/bar
+  A dir2/foo
+  ? dir2/untracked
 
   $ cd ..
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -54,6 +54,7 @@ 
   tags
   tip
   unbundle
+  uncopy
   unshelve
   update
   verify
@@ -356,6 +357,7 @@ 
   tags: template
   tip: patch, git, style, template
   unbundle: update
+  uncopy: include, exclude
   unshelve: abort, continue, interactive, keep, name, tool, date
   update: clean, check, merge, date, rev, tool
   verify: full
diff --git a/relnotes/next b/relnotes/next
--- a/relnotes/next
+++ b/relnotes/next
@@ -1,5 +1,7 @@ 
 == New Features ==
 
+ * `hg uncopy` can be used to unmark a file as copied.
+
 
 == New Experimental Features ==
 
diff --git a/relnotes/5.3 b/relnotes/5.3
--- a/relnotes/5.3
+++ b/relnotes/5.3
@@ -2,6 +2,7 @@ 
 
  * Windows will process hgrc files in %PROGRAMDATA%\Mercurial\hgrc.d.
 
+ * `hg uncopy` can be used to unmark a file as copied.
 
 == New Experimental Features ==
 
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -7492,6 +7492,19 @@ 
 
 
 @command(
+    b'uncopy',
+    walkopts,
+    _(b'[OPTION]... DEST...'),
+    helpcategory=command.CATEGORY_FILE_CONTENTS,
+)
+def uncopy(ui, repo, *pats, **opts):
+    """unmark files as copied"""
+    opts = pycompat.byteskwargs(opts)
+    with repo.wlock(False):
+        return cmdutil.uncopy(ui, repo, pats, opts)
+
+
+@command(
     b'unshelve',
     [
         (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -1694,6 +1694,25 @@ 
     return errors != 0
 
 
+def uncopy(ui, repo, pats, opts):
+    ctx = repo[None]
+
+    match = scmutil.match(ctx, pats, opts)
+
+    current_copies = ctx.p1copies()
+    current_copies.update(ctx.p2copies())
+
+    uipathfn = scmutil.getuipathfn(repo)
+    for f in ctx.walk(match):
+        if f in current_copies:
+            ctx[f].markcopied(None)
+        elif match.exact(f):
+            ui.warn(
+                _(b'%s: not uncopying - file is not marked as copied\n')
+                % uipathfn(f)
+            )
+
+
 ## facility to let extension process additional data into an import patch
 # list of identifier to be executed in order
 extrapreimport = []  # run before commit