D8030: uncopy: add support for unmarking committed copies

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
30 messages Options
12
Reply | Threaded
Open this post in threaded view
|

D8030: uncopy: add support for unmarking committed copies

marmoute (Pierre-Yves David)
martinvonz created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  The simplest way I'm aware of to unmark a file as copied after
  committing is this:
 
    hg uncommit --keep <dest>
    hg forget <dest>
    hg add <dest>
    hg amend
 
  This patch teaches `hg uncopy` a `-r` argument to simplify that into:
 
    hg uncopy -r . <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.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  mercurial/cmdutil.py
  mercurial/commands.py
  mercurial/context.py
  relnotes/5.3
  relnotes/next
  tests/test-completion.t
  tests/test-copy.t
  tests/test-rename-after-merge.t

CHANGE DETAILS

diff --git a/tests/test-rename-after-merge.t b/tests/test-rename-after-merge.t
--- a/tests/test-rename-after-merge.t
+++ b/tests/test-rename-after-merge.t
@@ -120,4 +120,10 @@
   $ hg log -r tip -C -v | grep copies
   copies:      b2 (b1)
 
+Test unmarking copies in merge commit
+
+  $ hg uncopy -r . b2
+  abort: cannot unmark copy in merge commit
+  [255]
+
   $ cd ..
diff --git a/tests/test-copy.t b/tests/test-copy.t
--- a/tests/test-copy.t
+++ b/tests/test-copy.t
@@ -319,5 +319,56 @@
   A dir2/bar
   A dir2/foo
   ? dir2/untracked
+# Clean up for next test
+  $ hg forget dir2
+  removing dir2/bar
+  removing dir2/foo
+  $ rm -r dir2
+
+Test uncopy on committed copies
+
+# Commit some copies
+  $ hg cp bar baz
+  $ hg cp bar qux
+  $ hg ci -m copies
+  $ hg st -C --change .
+  A baz
+    bar
+  A qux
+    bar
+  $ base=$(hg log -r '.^' -T '{rev}')
+  $ hg log -G -T '{rev}:{node|short} {desc}\n' -r $base:
+  @  5:a612dc2edfda copies
+  |
+  o  4:4800b1f1f38e add dir/
+  |
+  ~
+# Add a dirty change on top to show that it's unaffected
+  $ echo dirty >> baz
+  $ hg st
+  M baz
+  $ cat baz
+  bleah
+  dirty
+  $ hg uncopy -r . baz
+  saved backup bundle to $TESTTMP/part2/.hg/strip-backup/a612dc2edfda-e36b4448-uncopy.hg
+# The unwanted copy is no longer recorded, but the unrelated one is
+  $ hg st -C --change .
+  A baz
+  A qux
+    bar
+# The old commit is gone and we have updated to the new commit
+  $ hg log -G -T '{rev}:{node|short} {desc}\n' -r $base:
+  @  5:c45090e5effe copies
+  |
+  o  4:4800b1f1f38e add dir/
+  |
+  ~
+# Working copy still has the uncommitted change
+  $ hg st
+  M baz
+  $ cat baz
+  bleah
+  dirty
 
   $ cd ..
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -357,7 +357,7 @@
   tags: template
   tip: patch, git, style, template
   unbundle: update
-  uncopy: include, exclude
+  uncopy: rev, 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,6 +1,7 @@
 == New Features ==
 
- * `hg uncopy` can be used to unmark a file as copied.
+ * `hg uncopy` can be used to unmark a file as copied. Use `hg uncopy -r REV`
+   to unmark already committed copies.
 
 
 == New Experimental Features ==
diff --git a/relnotes/5.3 b/relnotes/5.3
--- a/relnotes/5.3
+++ b/relnotes/5.3
@@ -2,7 +2,8 @@
 
  * Windows will process hgrc files in %PROGRAMDATA%\Mercurial\hgrc.d.
 
- * `hg uncopy` can be used to unmark a file as copied.
+ * `hg uncopy` can be used to unmark a file as copied. Use `hg uncopy -r REV`
+   to unmark already committed copies.
 
 == New Experimental Features ==
 
diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -2488,6 +2488,17 @@
             editor=editor,
         )
 
+    def tomemctx_for_amend(self, precursor):
+        extra = precursor.extra().copy()
+        extra[b'amend_source'] = precursor.hex()
+        return self.tomemctx(
+            text=precursor.description(),
+            branch=precursor.branch(),
+            extra=extra,
+            date=precursor.date(),
+            user=precursor.user(),
+        )
+
     def isdirty(self, path):
         return path in self._cache
 
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -7493,7 +7493,8 @@
 
 @command(
     b'uncopy',
-    walkopts,
+    [(b'r', b'rev', b'', _(b'unmark copies in the given revision'), _(b'REV'))]
+    + walkopts,
     _(b'[OPTION]... DEST...'),
     helpcategory=command.CATEGORY_FILE_CONTENTS,
 )
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -1695,7 +1695,23 @@
 
 
 def uncopy(ui, repo, pats, opts):
-    ctx = repo[None]
+    rev = opts[b'rev']
+    if rev:
+        ctx = scmutil.revsingle(repo, rev)
+    else:
+        ctx = repo[None]
+    if ctx.rev() is None:
+        new_ctx = ctx
+    else:
+        if len(ctx.parents()) > 1:
+            raise error.Abort(_(b'cannot unmark copy in merge commit'))
+        # avoid cycle context -> subrepo -> cmdutil
+        from . import context
+
+        rewriteutil.precheck(repo, [ctx.rev()], b'uncopy')
+        new_ctx = context.overlayworkingctx(repo)
+        new_ctx.setbase(ctx.p1())
+        mergemod.graft(repo, ctx, wctx=new_ctx)
 
     match = scmutil.match(ctx, pats, opts)
 
@@ -1705,13 +1721,24 @@
     uipathfn = scmutil.getuipathfn(repo)
     for f in ctx.walk(match):
         if f in current_copies:
-            ctx[f].markcopied(None)
+            new_ctx[f].markcopied(None)
         elif match.exact(f):
             ui.warn(
                 _(b'%s: not uncopying - file is not marked as copied\n')
                 % uipathfn(f)
             )
 
+    if ctx.rev() is not None:
+        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'uncopy', fixphase=True)
+
 
 ## facility to let extension process additional data into an import patch
 # list of identifier to be executed in order



To: martinvonz, #hg-reviewers
Cc: mercurial-devel
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

D8030: uncopy: add support for unmarking committed copies

marmoute (Pierre-Yves David)
martinvonz updated this revision to Diff 19670.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D8030?vs=19663&id=19670

BRANCH
  default

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

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

AFFECTED FILES
  mercurial/cmdutil.py
  mercurial/commands.py
  mercurial/context.py
  relnotes/next
  tests/test-completion.t
  tests/test-copy.t
  tests/test-rename-after-merge.t

CHANGE DETAILS

diff --git a/tests/test-rename-after-merge.t b/tests/test-rename-after-merge.t
--- a/tests/test-rename-after-merge.t
+++ b/tests/test-rename-after-merge.t
@@ -120,4 +120,10 @@
   $ hg log -r tip -C -v | grep copies
   copies:      b2 (b1)
 
+Test unmarking copies in merge commit
+
+  $ hg uncopy -r . b2
+  abort: cannot unmark copy in merge commit
+  [255]
+
   $ cd ..
diff --git a/tests/test-copy.t b/tests/test-copy.t
--- a/tests/test-copy.t
+++ b/tests/test-copy.t
@@ -319,5 +319,56 @@
   A dir2/bar
   A dir2/foo
   ? dir2/untracked
+# Clean up for next test
+  $ hg forget dir2
+  removing dir2/bar
+  removing dir2/foo
+  $ rm -r dir2
+
+Test uncopy on committed copies
+
+# Commit some copies
+  $ hg cp bar baz
+  $ hg cp bar qux
+  $ hg ci -m copies
+  $ hg st -C --change .
+  A baz
+    bar
+  A qux
+    bar
+  $ base=$(hg log -r '.^' -T '{rev}')
+  $ hg log -G -T '{rev}:{node|short} {desc}\n' -r $base:
+  @  5:a612dc2edfda copies
+  |
+  o  4:4800b1f1f38e add dir/
+  |
+  ~
+# Add a dirty change on top to show that it's unaffected
+  $ echo dirty >> baz
+  $ hg st
+  M baz
+  $ cat baz
+  bleah
+  dirty
+  $ hg uncopy -r . baz
+  saved backup bundle to $TESTTMP/part2/.hg/strip-backup/a612dc2edfda-e36b4448-uncopy.hg
+# The unwanted copy is no longer recorded, but the unrelated one is
+  $ hg st -C --change .
+  A baz
+  A qux
+    bar
+# The old commit is gone and we have updated to the new commit
+  $ hg log -G -T '{rev}:{node|short} {desc}\n' -r $base:
+  @  5:c45090e5effe copies
+  |
+  o  4:4800b1f1f38e add dir/
+  |
+  ~
+# Working copy still has the uncommitted change
+  $ hg st
+  M baz
+  $ cat baz
+  bleah
+  dirty
 
   $ cd ..
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -357,7 +357,7 @@
   tags: template
   tip: patch, git, style, template
   unbundle: update
-  uncopy: include, exclude
+  uncopy: rev, 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,6 +1,7 @@
 == New Features ==
 
- * `hg uncopy` can be used to unmark a file as copied.
+ * `hg uncopy` can be used to unmark a file as copied. Use `hg uncopy -r REV`
+   to unmark already committed copies.
 
 
 == New Experimental Features ==
diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -2488,6 +2488,17 @@
             editor=editor,
         )
 
+    def tomemctx_for_amend(self, precursor):
+        extra = precursor.extra().copy()
+        extra[b'amend_source'] = precursor.hex()
+        return self.tomemctx(
+            text=precursor.description(),
+            branch=precursor.branch(),
+            extra=extra,
+            date=precursor.date(),
+            user=precursor.user(),
+        )
+
     def isdirty(self, path):
         return path in self._cache
 
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -7493,7 +7493,8 @@
 
 @command(
     b'uncopy',
-    walkopts,
+    [(b'r', b'rev', b'', _(b'unmark copies in the given revision'), _(b'REV'))]
+    + walkopts,
     _(b'[OPTION]... DEST...'),
     helpcategory=command.CATEGORY_FILE_CONTENTS,
 )
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -1695,7 +1695,23 @@
 
 
 def uncopy(ui, repo, pats, opts):
-    ctx = repo[None]
+    rev = opts[b'rev']
+    if rev:
+        ctx = scmutil.revsingle(repo, rev)
+    else:
+        ctx = repo[None]
+    if ctx.rev() is None:
+        new_ctx = ctx
+    else:
+        if len(ctx.parents()) > 1:
+            raise error.Abort(_(b'cannot unmark copy in merge commit'))
+        # avoid cycle context -> subrepo -> cmdutil
+        from . import context
+
+        rewriteutil.precheck(repo, [ctx.rev()], b'uncopy')
+        new_ctx = context.overlayworkingctx(repo)
+        new_ctx.setbase(ctx.p1())
+        mergemod.graft(repo, ctx, wctx=new_ctx)
 
     match = scmutil.match(ctx, pats, opts)
 
@@ -1705,13 +1721,24 @@
     uipathfn = scmutil.getuipathfn(repo)
     for f in ctx.walk(match):
         if f in current_copies:
-            ctx[f].markcopied(None)
+            new_ctx[f].markcopied(None)
         elif match.exact(f):
             ui.warn(
                 _(b'%s: not uncopying - file is not marked as copied\n')
                 % uipathfn(f)
             )
 
+    if ctx.rev() is not None:
+        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'uncopy', fixphase=True)
+
 
 ## facility to let extension process additional data into an import patch
 # list of identifier to be executed in order



To: martinvonz, #hg-reviewers
Cc: mercurial-devel
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

D8030: uncopy: add support for unmarking committed copies

marmoute (Pierre-Yves David)
In reply to this post by marmoute (Pierre-Yves David)
durin42 added a comment.


  I'm conflicted on rolling the `uncopy && amend` action into the uncopy command like this. Could users not (without this patch) do `hg uncopy foo && hg amend`?

REPOSITORY
  rHG Mercurial

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

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

To: martinvonz, #hg-reviewers
Cc: durin42, mercurial-devel
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

D8030: uncopy: add support for unmarking committed copies

marmoute (Pierre-Yves David)
In reply to this post by marmoute (Pierre-Yves David)
martinvonz added a comment.


  In D8030#118480 <https://phab.mercurial-scm.org/D8030#118480>, @durin42 wrote:
 
  > I'm conflicted on rolling the `uncopy && amend` action into the uncopy command like this. Could users not (without this patch) do `hg uncopy foo && hg amend`?
 
  I don't think so, because it's not marked as a copy in the working copy, right?

REPOSITORY
  rHG Mercurial

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

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

To: martinvonz, #hg-reviewers
Cc: durin42, mercurial-devel
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

D8030: uncopy: add support for unmarking committed copies

marmoute (Pierre-Yves David)
In reply to this post by marmoute (Pierre-Yves David)
durin42 added a comment.
durin42 accepted this revision as: durin42.


  In D8030#118489 <https://phab.mercurial-scm.org/D8030#118489>, @martinvonz wrote:
 
  > In D8030#118480 <https://phab.mercurial-scm.org/D8030#118480>, @durin42 wrote:
  >
  >> I'm conflicted on rolling the `uncopy && amend` action into the uncopy command like this. Could users not (without this patch) do `hg uncopy foo && hg amend`?
  >
  > I don't think so, because it's not marked as a copy in the working copy, right?
 
  Oh. Ew.
 
  I'm really not sure how I feel about that, but it does feel like this change is probably the best path forward.
 
  I'm out of time to review today (need to go make supper), but I'm at least +0 on this patch...

REPOSITORY
  rHG Mercurial

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

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

To: martinvonz, #hg-reviewers, durin42
Cc: durin42, mercurial-devel
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

D8030: uncopy: add support for unmarking committed copies

marmoute (Pierre-Yves David)
In reply to this post by marmoute (Pierre-Yves David)
martinvonz added a comment.


  In D8030#118542 <https://phab.mercurial-scm.org/D8030#118542>, @durin42 wrote:
 
  > In D8030#118489 <https://phab.mercurial-scm.org/D8030#118489>, @martinvonz wrote:
  >
  >> In D8030#118480 <https://phab.mercurial-scm.org/D8030#118480>, @durin42 wrote:
  >>
  >>> I'm conflicted on rolling the `uncopy && amend` action into the uncopy command like this. Could users not (without this patch) do `hg uncopy foo && hg amend`?
  >>
  >> I don't think so, because it's not marked as a copy in the working copy, right?
  >
  > Oh. Ew.
  > I'm really not sure how I feel about that, but it does feel like this change is probably the best path forward.
  > I'm out of time to review today (need to go make supper), but I'm at least +0 on this patch...
 
  I've started to think that the working copy should really be a commit and I find that helpful. If you think about it that way, it makes sense that `hg uncopy foo` (or more explicitly `hg uncopy -r 'wdir()' foo` unmarks copies in the working copy and `hg uncopy -r <some other commit> foo` unmarks copies in some other commit.

REPOSITORY
  rHG Mercurial

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

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

To: martinvonz, #hg-reviewers, durin42
Cc: durin42, mercurial-devel
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

D8030: uncopy: add support for unmarking committed copies

marmoute (Pierre-Yves David)
In reply to this post by marmoute (Pierre-Yves David)
martinvonz updated this revision to Diff 19693.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D8030?vs=19670&id=19693

BRANCH
  default

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

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

AFFECTED FILES
  mercurial/cmdutil.py
  mercurial/commands.py
  mercurial/context.py
  relnotes/next
  tests/test-completion.t
  tests/test-copy.t
  tests/test-rename-after-merge.t

CHANGE DETAILS

diff --git a/tests/test-rename-after-merge.t b/tests/test-rename-after-merge.t
--- a/tests/test-rename-after-merge.t
+++ b/tests/test-rename-after-merge.t
@@ -120,4 +120,10 @@
   $ hg log -r tip -C -v | grep copies
   copies:      b2 (b1)
 
+Test unmarking copies in merge commit
+
+  $ hg uncopy -r . b2
+  abort: cannot unmark copy in merge commit
+  [255]
+
   $ cd ..
diff --git a/tests/test-copy.t b/tests/test-copy.t
--- a/tests/test-copy.t
+++ b/tests/test-copy.t
@@ -319,5 +319,56 @@
   A dir2/bar
   A dir2/foo
   ? dir2/untracked
+# Clean up for next test
+  $ hg forget dir2
+  removing dir2/bar
+  removing dir2/foo
+  $ rm -r dir2
+
+Test uncopy on committed copies
+
+# Commit some copies
+  $ hg cp bar baz
+  $ hg cp bar qux
+  $ hg ci -m copies
+  $ hg st -C --change .
+  A baz
+    bar
+  A qux
+    bar
+  $ base=$(hg log -r '.^' -T '{rev}')
+  $ hg log -G -T '{rev}:{node|short} {desc}\n' -r $base:
+  @  5:a612dc2edfda copies
+  |
+  o  4:4800b1f1f38e add dir/
+  |
+  ~
+# Add a dirty change on top to show that it's unaffected
+  $ echo dirty >> baz
+  $ hg st
+  M baz
+  $ cat baz
+  bleah
+  dirty
+  $ hg uncopy -r . baz
+  saved backup bundle to $TESTTMP/part2/.hg/strip-backup/a612dc2edfda-e36b4448-uncopy.hg
+# The unwanted copy is no longer recorded, but the unrelated one is
+  $ hg st -C --change .
+  A baz
+  A qux
+    bar
+# The old commit is gone and we have updated to the new commit
+  $ hg log -G -T '{rev}:{node|short} {desc}\n' -r $base:
+  @  5:c45090e5effe copies
+  |
+  o  4:4800b1f1f38e add dir/
+  |
+  ~
+# Working copy still has the uncommitted change
+  $ hg st
+  M baz
+  $ cat baz
+  bleah
+  dirty
 
   $ cd ..
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -357,7 +357,7 @@
   tags: template
   tip: patch, git, style, template
   unbundle: update
-  uncopy: include, exclude
+  uncopy: rev, 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,6 +1,7 @@
 == New Features ==
 
- * `hg uncopy` can be used to unmark a file as copied.
+ * `hg uncopy` can be used to unmark a file as copied. Use `hg uncopy -r REV`
+   to unmark already committed copies.
 
 
 == New Experimental Features ==
diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -2488,6 +2488,17 @@
             editor=editor,
         )
 
+    def tomemctx_for_amend(self, precursor):
+        extra = precursor.extra().copy()
+        extra[b'amend_source'] = precursor.hex()
+        return self.tomemctx(
+            text=precursor.description(),
+            branch=precursor.branch(),
+            extra=extra,
+            date=precursor.date(),
+            user=precursor.user(),
+        )
+
     def isdirty(self, path):
         return path in self._cache
 
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -7495,7 +7495,8 @@
 
 @command(
     b'uncopy',
-    walkopts,
+    [(b'r', b'rev', b'', _(b'unmark copies in the given revision'), _(b'REV'))]
+    + walkopts,
     _(b'[OPTION]... DEST...'),
     helpcategory=command.CATEGORY_FILE_CONTENTS,
 )
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -1695,7 +1695,23 @@
 
 
 def uncopy(ui, repo, pats, opts):
-    ctx = repo[None]
+    rev = opts[b'rev']
+    if rev:
+        ctx = scmutil.revsingle(repo, rev)
+    else:
+        ctx = repo[None]
+    if ctx.rev() is None:
+        new_ctx = ctx
+    else:
+        if len(ctx.parents()) > 1:
+            raise error.Abort(_(b'cannot unmark copy in merge commit'))
+        # avoid cycle context -> subrepo -> cmdutil
+        from . import context
+
+        rewriteutil.precheck(repo, [ctx.rev()], b'uncopy')
+        new_ctx = context.overlayworkingctx(repo)
+        new_ctx.setbase(ctx.p1())
+        mergemod.graft(repo, ctx, wctx=new_ctx)
 
     match = scmutil.match(ctx, pats, opts)
 
@@ -1705,13 +1721,24 @@
     uipathfn = scmutil.getuipathfn(repo)
     for f in ctx.walk(match):
         if f in current_copies:
-            ctx[f].markcopied(None)
+            new_ctx[f].markcopied(None)
         elif match.exact(f):
             ui.warn(
                 _(b'%s: not uncopying - file is not marked as copied\n')
                 % uipathfn(f)
             )
 
+    if ctx.rev() is not None:
+        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'uncopy', fixphase=True)
+
 
 ## facility to let extension process additional data into an import patch
 # list of identifier to be executed in order



To: martinvonz, #hg-reviewers, durin42
Cc: durin42, mercurial-devel
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

D8030: uncopy: add support for unmarking committed copies

marmoute (Pierre-Yves David)
In reply to this post by marmoute (Pierre-Yves David)
pulkit added a comment.


  I don't feel very good with this because this makes `hg uncopy` a history editing command. Something like `hg uncopy --after` which makes the changes in wdir which can be committed afterwards sounds best, but I am not sure if that's possible.

REPOSITORY
  rHG Mercurial

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

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

To: martinvonz, #hg-reviewers, durin42
Cc: pulkit, durin42, mercurial-devel
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

D8030: uncopy: add support for unmarking committed copies

marmoute (Pierre-Yves David)
In reply to this post by marmoute (Pierre-Yves David)
martinvonz added a comment.


  In D8030#118662 <https://phab.mercurial-scm.org/D8030#118662>, @pulkit wrote:
 
  > I don't feel very good with this because this makes `hg uncopy` a history editing command.
 
  That's a good thing in my opinion :)
 
  > Something like `hg uncopy --after` which makes the changes in wdir which can be committed afterwards sounds best, but I am not sure if that's possible.
 
  Yeah, not really possible (Augie had the same comment on one of these patches).

REPOSITORY
  rHG Mercurial

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

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

To: martinvonz, #hg-reviewers, durin42
Cc: pulkit, durin42, mercurial-devel
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

D8030: uncopy: add support for unmarking committed copies

marmoute (Pierre-Yves David)
In reply to this post by marmoute (Pierre-Yves David)
martinvonz updated this revision to Diff 19726.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D8030?vs=19693&id=19726

BRANCH
  default

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

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

AFFECTED FILES
  mercurial/cmdutil.py
  mercurial/commands.py
  mercurial/context.py
  relnotes/next
  tests/test-completion.t
  tests/test-copy.t
  tests/test-rename-after-merge.t

CHANGE DETAILS

diff --git a/tests/test-rename-after-merge.t b/tests/test-rename-after-merge.t
--- a/tests/test-rename-after-merge.t
+++ b/tests/test-rename-after-merge.t
@@ -120,4 +120,10 @@
   $ hg log -r tip -C -v | grep copies
   copies:      b2 (b1)
 
+Test unmarking copies in merge commit
+
+  $ hg uncopy -r . b2
+  abort: cannot unmark copy in merge commit
+  [255]
+
   $ cd ..
diff --git a/tests/test-copy.t b/tests/test-copy.t
--- a/tests/test-copy.t
+++ b/tests/test-copy.t
@@ -319,5 +319,56 @@
   A dir2/bar
   A dir2/foo
   ? dir2/untracked
+# Clean up for next test
+  $ hg forget dir2
+  removing dir2/bar
+  removing dir2/foo
+  $ rm -r dir2
+
+Test uncopy on committed copies
+
+# Commit some copies
+  $ hg cp bar baz
+  $ hg cp bar qux
+  $ hg ci -m copies
+  $ hg st -C --change .
+  A baz
+    bar
+  A qux
+    bar
+  $ base=$(hg log -r '.^' -T '{rev}')
+  $ hg log -G -T '{rev}:{node|short} {desc}\n' -r $base:
+  @  5:a612dc2edfda copies
+  |
+  o  4:4800b1f1f38e add dir/
+  |
+  ~
+# Add a dirty change on top to show that it's unaffected
+  $ echo dirty >> baz
+  $ hg st
+  M baz
+  $ cat baz
+  bleah
+  dirty
+  $ hg uncopy -r . baz
+  saved backup bundle to $TESTTMP/part2/.hg/strip-backup/a612dc2edfda-e36b4448-uncopy.hg
+# The unwanted copy is no longer recorded, but the unrelated one is
+  $ hg st -C --change .
+  A baz
+  A qux
+    bar
+# The old commit is gone and we have updated to the new commit
+  $ hg log -G -T '{rev}:{node|short} {desc}\n' -r $base:
+  @  5:c45090e5effe copies
+  |
+  o  4:4800b1f1f38e add dir/
+  |
+  ~
+# Working copy still has the uncommitted change
+  $ hg st
+  M baz
+  $ cat baz
+  bleah
+  dirty
 
   $ cd ..
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -357,7 +357,7 @@
   tags: template
   tip: patch, git, style, template
   unbundle: update
-  uncopy: include, exclude
+  uncopy: rev, 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,6 +1,7 @@
 == New Features ==
 
- * `hg uncopy` can be used to unmark a file as copied.
+ * `hg uncopy` can be used to unmark a file as copied. Use `hg uncopy -r REV`
+   to unmark already committed copies.
 
 
 == New Experimental Features ==
diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -2488,6 +2488,17 @@
             editor=editor,
         )
 
+    def tomemctx_for_amend(self, precursor):
+        extra = precursor.extra().copy()
+        extra[b'amend_source'] = precursor.hex()
+        return self.tomemctx(
+            text=precursor.description(),
+            branch=precursor.branch(),
+            extra=extra,
+            date=precursor.date(),
+            user=precursor.user(),
+        )
+
     def isdirty(self, path):
         return path in self._cache
 
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -7495,7 +7495,8 @@
 
 @command(
     b'uncopy',
-    walkopts,
+    [(b'r', b'rev', b'', _(b'unmark copies in the given revision'), _(b'REV'))]
+    + walkopts,
     _(b'[OPTION]... DEST...'),
     helpcategory=command.CATEGORY_FILE_CONTENTS,
 )
@@ -7504,7 +7505,8 @@
 
     Unmark DEST as having copies of source files.
 
-    This command takes effect with the next commit.
+    This command takes effect with the next commit by default. Use
+    ``-r`` to unmark copies in an existing commit.
 
     Returns 0 on success, 1 if errors are encountered.
     """
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -1695,7 +1695,23 @@
 
 
 def uncopy(ui, repo, pats, opts):
-    ctx = repo[None]
+    rev = opts[b'rev']
+    if rev:
+        ctx = scmutil.revsingle(repo, rev)
+    else:
+        ctx = repo[None]
+    if ctx.rev() is None:
+        new_ctx = ctx
+    else:
+        if len(ctx.parents()) > 1:
+            raise error.Abort(_(b'cannot unmark copy in merge commit'))
+        # avoid cycle context -> subrepo -> cmdutil
+        from . import context
+
+        rewriteutil.precheck(repo, [ctx.rev()], b'uncopy')
+        new_ctx = context.overlayworkingctx(repo)
+        new_ctx.setbase(ctx.p1())
+        mergemod.graft(repo, ctx, wctx=new_ctx)
 
     match = scmutil.match(ctx, pats, opts)
 
@@ -1705,13 +1721,24 @@
     uipathfn = scmutil.getuipathfn(repo)
     for f in ctx.walk(match):
         if f in current_copies:
-            ctx[f].markcopied(None)
+            new_ctx[f].markcopied(None)
         elif match.exact(f):
             ui.warn(
                 _(b'%s: not uncopying - file is not marked as copied\n')
                 % uipathfn(f)
             )
 
+    if ctx.rev() is not None:
+        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'uncopy', fixphase=True)
+
 
 ## facility to let extension process additional data into an import patch
 # list of identifier to be executed in order



To: martinvonz, #hg-reviewers, durin42
Cc: pulkit, durin42, mercurial-devel
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

D8030: uncopy: add support for unmarking committed copies

marmoute (Pierre-Yves David)
In reply to this post by marmoute (Pierre-Yves David)
marmoute added a comment.


  >> Something like `hg uncopy --after` which makes the changes in wdir which can be committed afterwards sounds best, but I am not sure if that's possible.
  >
  > Yeah, not really possible (Augie had the same comment on one of these patches).
 
  Can we update the dirstate to represent such copy related information ? (both for adding or removing a copy).

REPOSITORY
  rHG Mercurial

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

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

To: martinvonz, #hg-reviewers, durin42
Cc: marmoute, pulkit, durin42, mercurial-devel
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

D8030: uncopy: add support for unmarking committed copies

marmoute (Pierre-Yves David)
In reply to this post by marmoute (Pierre-Yves David)
This revision now requires changes to proceed.
marmoute added a comment.
marmoute requested changes to this revision.


  In D8030#119816 <https://phab.mercurial-scm.org/D8030#119816>, @marmoute wrote:
 
  >>> Something like `hg uncopy --after` which makes the changes in wdir which can be committed afterwards sounds best, but I am not sure if that's possible.
  >>
  >> Yeah, not really possible (Augie had the same comment on one of these patches).
  >
  > Can we update the dirstate to represent such copy related information ? (both for adding or removing a copy).
 
  Something that really bother me with this kind of command rewriting history is that it can result in an absurd amount of rewrite.
 
  The usual way to change something in mercurial is:
 
  1. get on a clean revision
  2. make changes 1
  3. make changes 2
  4. make changes …
  5. make changes 42
  6. record the changes (commit, amend, etc…)
 
  If command like `hg copy` start rewriting history, each smallest atomic operation result rewriting one (or multiple) changesets. This is a slippery slope I would rather not walk on.
 
  I think we should explore being able to record this in dirstate and use regular amend to record the change.

INLINE COMMENTS

> cmdutil.py:1707
> +        if len(ctx.parents()) > 1:
> +            raise error.Abort(_(b'cannot unmark copy in merge commit'))
> +        # avoid cycle context -> subrepo -> cmdutil

Why can we do it for merge commit ?

> cmdutil.py:1708
> +            raise error.Abort(_(b'cannot unmark copy in merge commit'))
> +        # avoid cycle context -> subrepo -> cmdutil
> +        from . import context

I prefer this kind of shortcut to be exceptional. Maybe this need to move out of cmdutil ?

> cmdutil.py:1711
> +
> +        rewriteutil.precheck(repo, [ctx.rev()], b'uncopy')
> +        new_ctx = context.overlayworkingctx(repo)

bonus point for using precheck ☺

REPOSITORY
  rHG Mercurial

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

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

To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: marmoute, pulkit, durin42, mercurial-devel
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

D8030: uncopy: add support for unmarking committed copies

marmoute (Pierre-Yves David)
In reply to this post by marmoute (Pierre-Yves David)
martinvonz added a comment.
martinvonz marked 3 inline comments as done.


  In D8030#119844 <https://phab.mercurial-scm.org/D8030#119844>, @marmoute wrote:
 
  > In D8030#119816 <https://phab.mercurial-scm.org/D8030#119816>, @marmoute wrote:
  >
  >>>> Something like `hg uncopy --after` which makes the changes in wdir which can be committed afterwards sounds best, but I am not sure if that's possible.
  >>>
  >>> Yeah, not really possible (Augie had the same comment on one of these patches).
  >>
  >> Can we update the dirstate to represent such copy related information ? (both for adding or removing a copy).
  >
  > Something that really bother me with this kind of command rewriting history is that it can result in an absurd amount of rewrite.
  > The usual way to change something in mercurial is:
  >
  > 1. get on a clean revision
  > 2. make changes 1
  > 3. make changes 2
  > 4. make changes …
  > 5. make changes 42
  > 6. record the changes (commit, amend, etc…)
  >
  > If command like `hg copy` start rewriting history, each smallest atomic operation result rewriting one (or multiple) changesets. This is a slippery slope I would rather not walk on.
  > I think we should explore being able to record this in dirstate and use regular amend to record the change.
 
  I completely disagree :P I think it's a good thing to allow it to work on other commits. Of course it's better if the user thinks of it before they commit, but when they don't, we should still make it as easy as possible.

INLINE COMMENTS

> marmoute wrote in cmdutil.py:1707
> Why can we do it for merge commit ?

Because I didn't feel like implementing support for it yet. Can be done in a follow-up, IMO.

> marmoute wrote in cmdutil.py:1708
> I prefer this kind of shortcut to be exceptional. Maybe this need to move out of cmdutil ?

I agree (of course). There are already several cases in this file. I thought about the cycle for a few minutes and it wasn't clear how it should be broken. How do you suggest we resolve the cycle?

> marmoute wrote in cmdutil.py:1711
> bonus point for using precheck ☺

Thanks :)

REPOSITORY
  rHG Mercurial

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

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

To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: marmoute, pulkit, durin42, mercurial-devel
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

D8030: copy: add support for unmarking committed copies

marmoute (Pierre-Yves David)
In reply to this post by marmoute (Pierre-Yves David)
martinvonz edited the summary of this revision.
martinvonz retitled this revision from "uncopy: add support for unmarking committed copies" to "copy: add support for unmarking committed copies".
martinvonz marked 3 inline comments as done.
martinvonz updated this revision to Diff 20069.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D8030?vs=19726&id=20069

BRANCH
  default

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

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

AFFECTED FILES
  mercurial/cmdutil.py
  mercurial/commands.py
  mercurial/context.py
  relnotes/next
  tests/test-completion.t
  tests/test-copy.t
  tests/test-rename-after-merge.t

CHANGE DETAILS

diff --git a/tests/test-rename-after-merge.t b/tests/test-rename-after-merge.t
--- a/tests/test-rename-after-merge.t
+++ b/tests/test-rename-after-merge.t
@@ -120,4 +120,10 @@
   $ hg log -r tip -C -v | grep copies
   copies:      b2 (b1)
 
+Test unmarking copies in merge commit
+
+  $ hg copy --forget -r . b2
+  abort: cannot unmark copy in merge commit
+  [255]
+
   $ cd ..
diff --git a/tests/test-copy.t b/tests/test-copy.t
--- a/tests/test-copy.t
+++ b/tests/test-copy.t
@@ -319,5 +319,56 @@
   A dir2/bar
   A dir2/foo
   ? dir2/untracked
+# Clean up for next test
+  $ hg forget dir2
+  removing dir2/bar
+  removing dir2/foo
+  $ rm -r dir2
+
+Test uncopy on committed copies
+
+# Commit some copies
+  $ hg cp bar baz
+  $ hg cp bar qux
+  $ hg ci -m copies
+  $ hg st -C --change .
+  A baz
+    bar
+  A qux
+    bar
+  $ base=$(hg log -r '.^' -T '{rev}')
+  $ hg log -G -T '{rev}:{node|short} {desc}\n' -r $base:
+  @  5:a612dc2edfda copies
+  |
+  o  4:4800b1f1f38e add dir/
+  |
+  ~
+# Add a dirty change on top to show that it's unaffected
+  $ echo dirty >> baz
+  $ hg st
+  M baz
+  $ cat baz
+  bleah
+  dirty
+  $ hg copy --forget -r . baz
+  saved backup bundle to $TESTTMP/part2/.hg/strip-backup/a612dc2edfda-e36b4448-uncopy.hg
+# The unwanted copy is no longer recorded, but the unrelated one is
+  $ hg st -C --change .
+  A baz
+  A qux
+    bar
+# The old commit is gone and we have updated to the new commit
+  $ hg log -G -T '{rev}:{node|short} {desc}\n' -r $base:
+  @  5:c45090e5effe copies
+  |
+  o  4:4800b1f1f38e add dir/
+  |
+  ~
+# Working copy still has the uncommitted change
+  $ hg st
+  M baz
+  $ cat baz
+  bleah
+  dirty
 
   $ cd ..
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -255,7 +255,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: forget, after, force, include, exclude, dry-run
+  copy: forget, after, rev, force, include, exclude, dry-run
   debugancestor:
   debugapplystreamclonebundle:
   debugbuilddag: mergeable-file, overwritten-file, new-file
diff --git a/relnotes/next b/relnotes/next
--- a/relnotes/next
+++ b/relnotes/next
@@ -1,6 +1,7 @@
 == New Features ==
 
- * `hg copy --forget` can be used to unmark a file as copied.
+ * `hg copy --forget` can be used to unmark a file as copied. Use `hg
+   copy --forget -r REV` to unmark already committed copies.
 
 
 == New Experimental Features ==
diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -2487,6 +2487,17 @@
             editor=editor,
         )
 
+    def tomemctx_for_amend(self, precursor):
+        extra = precursor.extra().copy()
+        extra[b'amend_source'] = precursor.hex()
+        return self.tomemctx(
+            text=precursor.description(),
+            branch=precursor.branch(),
+            extra=extra,
+            date=precursor.date(),
+            user=precursor.user(),
+        )
+
     def isdirty(self, path):
         return path in self._cache
 
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -2312,6 +2312,13 @@
         (b'', b'forget', None, _(b'unmark a file as copied')),
         (b'A', b'after', None, _(b'record a copy that has already occurred')),
         (
+            b'r',
+            b'rev',
+            b'',
+            _(b'unmark copies in the given revision'),
+            _(b'REV'),
+        ),
+        (
             b'f',
             b'force',
             None,
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -1419,14 +1419,33 @@
     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):
+        rev = opts[b'rev']
+        if rev:
+            ctx = scmutil.revsingle(repo, rev)
+        else:
+            ctx = repo[None]
+        if ctx.rev() is None:
+            new_ctx = ctx
+        else:
+            if len(ctx.parents()) > 1:
+                raise error.Abort(_(b'cannot unmark copy in merge commit'))
+            # avoid cycle context -> subrepo -> cmdutil
+            from . import context
+
+            rewriteutil.precheck(repo, [ctx.rev()], b'uncopy')
+            new_ctx = context.overlayworkingctx(repo)
+            new_ctx.setbase(ctx.p1())
+            mergemod.graft(repo, ctx, wctx=new_ctx)
+
+        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:
-                wctx[f].markcopied(None)
+                new_ctx[f].markcopied(None)
             elif match.exact(f):
                 ui.warn(
                     _(
@@ -1434,8 +1453,25 @@
                     )
                     % uipathfn(f)
                 )
+
+        if ctx.rev() is not None:
+            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'uncopy', fixphase=True
+                )
+
         return
 
+    if opts.get(b'rev'):
+        raise error.Abort(_("--rev is only supported with --forget"))
+
     def walkpat(pat):
         srcs = []
         if after:



To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: marmoute, pulkit, durin42, mercurial-devel
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

D8030: copy: add support for unmarking committed copies

marmoute (Pierre-Yves David)
In reply to this post by marmoute (Pierre-Yves David)
martinvonz updated this revision to Diff 20082.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D8030?vs=20069&id=20082

BRANCH
  default

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

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

AFFECTED FILES
  mercurial/cmdutil.py
  mercurial/commands.py
  mercurial/context.py
  relnotes/next
  tests/test-completion.t
  tests/test-copy.t
  tests/test-rename-after-merge.t

CHANGE DETAILS

diff --git a/tests/test-rename-after-merge.t b/tests/test-rename-after-merge.t
--- a/tests/test-rename-after-merge.t
+++ b/tests/test-rename-after-merge.t
@@ -120,4 +120,10 @@
   $ hg log -r tip -C -v | grep copies
   copies:      b2 (b1)
 
+Test unmarking copies in merge commit
+
+  $ hg copy --forget -r . b2
+  abort: cannot unmark copy in merge commit
+  [255]
+
   $ cd ..
diff --git a/tests/test-copy.t b/tests/test-copy.t
--- a/tests/test-copy.t
+++ b/tests/test-copy.t
@@ -319,5 +319,56 @@
   A dir2/bar
   A dir2/foo
   ? dir2/untracked
+# Clean up for next test
+  $ hg forget dir2
+  removing dir2/bar
+  removing dir2/foo
+  $ rm -r dir2
+
+Test uncopy on committed copies
+
+# Commit some copies
+  $ hg cp bar baz
+  $ hg cp bar qux
+  $ hg ci -m copies
+  $ hg st -C --change .
+  A baz
+    bar
+  A qux
+    bar
+  $ base=$(hg log -r '.^' -T '{rev}')
+  $ hg log -G -T '{rev}:{node|short} {desc}\n' -r $base:
+  @  5:a612dc2edfda copies
+  |
+  o  4:4800b1f1f38e add dir/
+  |
+  ~
+# Add a dirty change on top to show that it's unaffected
+  $ echo dirty >> baz
+  $ hg st
+  M baz
+  $ cat baz
+  bleah
+  dirty
+  $ hg copy --forget -r . baz
+  saved backup bundle to $TESTTMP/part2/.hg/strip-backup/a612dc2edfda-e36b4448-uncopy.hg
+# The unwanted copy is no longer recorded, but the unrelated one is
+  $ hg st -C --change .
+  A baz
+  A qux
+    bar
+# The old commit is gone and we have updated to the new commit
+  $ hg log -G -T '{rev}:{node|short} {desc}\n' -r $base:
+  @  5:c45090e5effe copies
+  |
+  o  4:4800b1f1f38e add dir/
+  |
+  ~
+# Working copy still has the uncommitted change
+  $ hg st
+  M baz
+  $ cat baz
+  bleah
+  dirty
 
   $ cd ..
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -256,7 +256,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: forget, after, force, include, exclude, dry-run
+  copy: forget, after, rev, force, include, exclude, dry-run
   debugancestor:
   debugapplystreamclonebundle:
   debugbuilddag: mergeable-file, overwritten-file, new-file
diff --git a/relnotes/next b/relnotes/next
--- a/relnotes/next
+++ b/relnotes/next
@@ -3,7 +3,8 @@
  * `hg purge`/`hg clean` can now delete ignored files instead of
    untracked files, with the new -i flag.
 
- * `hg copy --forget` can be used to unmark a file as copied.
+ * `hg copy --forget` can be used to unmark a file as copied. Use `hg
+   copy --forget -r REV` to unmark already committed copies.
 
 
 == New Experimental Features ==
diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -2487,6 +2487,17 @@
             editor=editor,
         )
 
+    def tomemctx_for_amend(self, precursor):
+        extra = precursor.extra().copy()
+        extra[b'amend_source'] = precursor.hex()
+        return self.tomemctx(
+            text=precursor.description(),
+            branch=precursor.branch(),
+            extra=extra,
+            date=precursor.date(),
+            user=precursor.user(),
+        )
+
     def isdirty(self, path):
         return path in self._cache
 
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -2312,6 +2312,13 @@
         (b'', b'forget', None, _(b'unmark a file as copied')),
         (b'A', b'after', None, _(b'record a copy that has already occurred')),
         (
+            b'r',
+            b'rev',
+            b'',
+            _(b'unmark copies in the given revision'),
+            _(b'REV'),
+        ),
+        (
             b'f',
             b'force',
             None,
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -1419,14 +1419,33 @@
     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):
+        rev = opts[b'rev']
+        if rev:
+            ctx = scmutil.revsingle(repo, rev)
+        else:
+            ctx = repo[None]
+        if ctx.rev() is None:
+            new_ctx = ctx
+        else:
+            if len(ctx.parents()) > 1:
+                raise error.Abort(_(b'cannot unmark copy in merge commit'))
+            # avoid cycle context -> subrepo -> cmdutil
+            from . import context
+
+            rewriteutil.precheck(repo, [ctx.rev()], b'uncopy')
+            new_ctx = context.overlayworkingctx(repo)
+            new_ctx.setbase(ctx.p1())
+            mergemod.graft(repo, ctx, wctx=new_ctx)
+
+        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:
-                wctx[f].markcopied(None)
+                new_ctx[f].markcopied(None)
             elif match.exact(f):
                 ui.warn(
                     _(
@@ -1434,8 +1453,25 @@
                     )
                     % uipathfn(f)
                 )
+
+        if ctx.rev() is not None:
+            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'uncopy', fixphase=True
+                )
+
         return
 
+    if opts.get(b'rev'):
+        raise error.Abort(_("--rev is only supported with --forget"))
+
     def walkpat(pat):
         srcs = []
         if after:



To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: marmoute, pulkit, durin42, mercurial-devel
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

D8030: copy: add support for unmarking committed copies

marmoute (Pierre-Yves David)
In reply to this post by marmoute (Pierre-Yves David)
martinvonz updated this revision to Diff 20149.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D8030?vs=20082&id=20149

BRANCH
  default

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

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

AFFECTED FILES
  mercurial/cmdutil.py
  mercurial/commands.py
  mercurial/context.py
  relnotes/next
  tests/test-completion.t
  tests/test-copy.t
  tests/test-rename-after-merge.t

CHANGE DETAILS

diff --git a/tests/test-rename-after-merge.t b/tests/test-rename-after-merge.t
--- a/tests/test-rename-after-merge.t
+++ b/tests/test-rename-after-merge.t
@@ -120,4 +120,10 @@
   $ hg log -r tip -C -v | grep copies
   copies:      b2 (b1)
 
+Test unmarking copies in merge commit
+
+  $ hg copy --forget -r . b2
+  abort: cannot unmark copy in merge commit
+  [255]
+
   $ cd ..
diff --git a/tests/test-copy.t b/tests/test-copy.t
--- a/tests/test-copy.t
+++ b/tests/test-copy.t
@@ -319,5 +319,56 @@
   A dir2/bar
   A dir2/foo
   ? dir2/untracked
+# Clean up for next test
+  $ hg forget dir2
+  removing dir2/bar
+  removing dir2/foo
+  $ rm -r dir2
+
+Test uncopy on committed copies
+
+# Commit some copies
+  $ hg cp bar baz
+  $ hg cp bar qux
+  $ hg ci -m copies
+  $ hg st -C --change .
+  A baz
+    bar
+  A qux
+    bar
+  $ base=$(hg log -r '.^' -T '{rev}')
+  $ hg log -G -T '{rev}:{node|short} {desc}\n' -r $base:
+  @  5:a612dc2edfda copies
+  |
+  o  4:4800b1f1f38e add dir/
+  |
+  ~
+# Add a dirty change on top to show that it's unaffected
+  $ echo dirty >> baz
+  $ hg st
+  M baz
+  $ cat baz
+  bleah
+  dirty
+  $ hg copy --forget -r . baz
+  saved backup bundle to $TESTTMP/part2/.hg/strip-backup/a612dc2edfda-e36b4448-uncopy.hg
+# The unwanted copy is no longer recorded, but the unrelated one is
+  $ hg st -C --change .
+  A baz
+  A qux
+    bar
+# The old commit is gone and we have updated to the new commit
+  $ hg log -G -T '{rev}:{node|short} {desc}\n' -r $base:
+  @  5:c45090e5effe copies
+  |
+  o  4:4800b1f1f38e add dir/
+  |
+  ~
+# Working copy still has the uncommitted change
+  $ hg st
+  M baz
+  $ cat baz
+  bleah
+  dirty
 
   $ cd ..
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -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: forget, after, force, include, exclude, dry-run
+  copy: forget, after, rev, force, include, exclude, dry-run
   debugancestor:
   debugapplystreamclonebundle:
   debugbuilddag: mergeable-file, overwritten-file, new-file
diff --git a/relnotes/next b/relnotes/next
--- a/relnotes/next
+++ b/relnotes/next
@@ -12,7 +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.
+ * `hg copy --forget` can be used to unmark a file as copied. Use `hg
+   copy --forget -r REV` to unmark already committed copies.
 
 
 == New Experimental Features ==
diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -2487,6 +2487,17 @@
             editor=editor,
         )
 
+    def tomemctx_for_amend(self, precursor):
+        extra = precursor.extra().copy()
+        extra[b'amend_source'] = precursor.hex()
+        return self.tomemctx(
+            text=precursor.description(),
+            branch=precursor.branch(),
+            extra=extra,
+            date=precursor.date(),
+            user=precursor.user(),
+        )
+
     def isdirty(self, path):
         return path in self._cache
 
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -2312,6 +2312,13 @@
         (b'', b'forget', None, _(b'unmark a file as copied')),
         (b'A', b'after', None, _(b'record a copy that has already occurred')),
         (
+            b'r',
+            b'rev',
+            b'',
+            _(b'unmark copies in the given revision'),
+            _(b'REV'),
+        ),
+        (
             b'f',
             b'force',
             None,
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -1426,14 +1426,33 @@
     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):
+        rev = opts[b'rev']
+        if rev:
+            ctx = scmutil.revsingle(repo, rev)
+        else:
+            ctx = repo[None]
+        if ctx.rev() is None:
+            new_ctx = ctx
+        else:
+            if len(ctx.parents()) > 1:
+                raise error.Abort(_(b'cannot unmark copy in merge commit'))
+            # avoid cycle context -> subrepo -> cmdutil
+            from . import context
+
+            rewriteutil.precheck(repo, [ctx.rev()], b'uncopy')
+            new_ctx = context.overlayworkingctx(repo)
+            new_ctx.setbase(ctx.p1())
+            mergemod.graft(repo, ctx, wctx=new_ctx)
+
+        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:
-                wctx[f].markcopied(None)
+                new_ctx[f].markcopied(None)
             elif match.exact(f):
                 ui.warn(
                     _(
@@ -1441,8 +1460,25 @@
                     )
                     % uipathfn(f)
                 )
+
+        if ctx.rev() is not None:
+            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'uncopy', fixphase=True
+                )
+
         return
 
+    if opts.get(b'rev'):
+        raise error.Abort(_("--rev is only supported with --forget"))
+
     def walkpat(pat):
         srcs = []
         if after:



To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: marmoute, pulkit, durin42, mercurial-devel
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

D8030: copy: add support for unmarking committed copies

marmoute (Pierre-Yves David)
In reply to this post by marmoute (Pierre-Yves David)
durin42 added a comment.
durin42 accepted this revision.


  I remain happy with this.

REPOSITORY
  rHG Mercurial

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

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

To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: marmoute, pulkit, durin42, mercurial-devel
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

D8030: copy: add support for unmarking committed copies

marmoute (Pierre-Yves David)
In reply to this post by marmoute (Pierre-Yves David)
martinvonz updated this revision to Diff 20192.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D8030?vs=20149&id=20192

BRANCH
  default

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

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

AFFECTED FILES
  mercurial/cmdutil.py
  mercurial/commands.py
  mercurial/context.py
  relnotes/next
  tests/test-completion.t
  tests/test-copy.t
  tests/test-rename-after-merge.t

CHANGE DETAILS

diff --git a/tests/test-rename-after-merge.t b/tests/test-rename-after-merge.t
--- a/tests/test-rename-after-merge.t
+++ b/tests/test-rename-after-merge.t
@@ -120,4 +120,10 @@
   $ hg log -r tip -C -v | grep copies
   copies:      b2 (b1)
 
+Test unmarking copies in merge commit
+
+  $ hg copy --forget -r . b2
+  abort: cannot unmark copy in merge commit
+  [255]
+
   $ cd ..
diff --git a/tests/test-copy.t b/tests/test-copy.t
--- a/tests/test-copy.t
+++ b/tests/test-copy.t
@@ -319,5 +319,56 @@
   A dir2/bar
   A dir2/foo
   ? dir2/untracked
+# Clean up for next test
+  $ hg forget dir2
+  removing dir2/bar
+  removing dir2/foo
+  $ rm -r dir2
+
+Test uncopy on committed copies
+
+# Commit some copies
+  $ hg cp bar baz
+  $ hg cp bar qux
+  $ hg ci -m copies
+  $ hg st -C --change .
+  A baz
+    bar
+  A qux
+    bar
+  $ base=$(hg log -r '.^' -T '{rev}')
+  $ hg log -G -T '{rev}:{node|short} {desc}\n' -r $base:
+  @  5:a612dc2edfda copies
+  |
+  o  4:4800b1f1f38e add dir/
+  |
+  ~
+# Add a dirty change on top to show that it's unaffected
+  $ echo dirty >> baz
+  $ hg st
+  M baz
+  $ cat baz
+  bleah
+  dirty
+  $ hg copy --forget -r . baz
+  saved backup bundle to $TESTTMP/part2/.hg/strip-backup/a612dc2edfda-e36b4448-uncopy.hg
+# The unwanted copy is no longer recorded, but the unrelated one is
+  $ hg st -C --change .
+  A baz
+  A qux
+    bar
+# The old commit is gone and we have updated to the new commit
+  $ hg log -G -T '{rev}:{node|short} {desc}\n' -r $base:
+  @  5:c45090e5effe copies
+  |
+  o  4:4800b1f1f38e add dir/
+  |
+  ~
+# Working copy still has the uncommitted change
+  $ hg st
+  M baz
+  $ cat baz
+  bleah
+  dirty
 
   $ cd ..
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -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: forget, after, force, include, exclude, dry-run
+  copy: forget, after, rev, force, include, exclude, dry-run
   debugancestor:
   debugapplystreamclonebundle:
   debugbuilddag: mergeable-file, overwritten-file, new-file
diff --git a/relnotes/next b/relnotes/next
--- a/relnotes/next
+++ b/relnotes/next
@@ -12,7 +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.
+ * `hg copy --forget` can be used to unmark a file as copied. Use `hg
+   copy --forget -r REV` to unmark already committed copies.
 
 
 == New Experimental Features ==
diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -2487,6 +2487,17 @@
             editor=editor,
         )
 
+    def tomemctx_for_amend(self, precursor):
+        extra = precursor.extra().copy()
+        extra[b'amend_source'] = precursor.hex()
+        return self.tomemctx(
+            text=precursor.description(),
+            branch=precursor.branch(),
+            extra=extra,
+            date=precursor.date(),
+            user=precursor.user(),
+        )
+
     def isdirty(self, path):
         return path in self._cache
 
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -2312,6 +2312,13 @@
         (b'', b'forget', None, _(b'unmark a file as copied')),
         (b'A', b'after', None, _(b'record a copy that has already occurred')),
         (
+            b'r',
+            b'rev',
+            b'',
+            _(b'unmark copies in the given revision'),
+            _(b'REV'),
+        ),
+        (
             b'f',
             b'force',
             None,
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -1427,14 +1427,33 @@
     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):
+        rev = opts[b'rev']
+        if rev:
+            ctx = scmutil.revsingle(repo, rev)
+        else:
+            ctx = repo[None]
+        if ctx.rev() is None:
+            new_ctx = ctx
+        else:
+            if len(ctx.parents()) > 1:
+                raise error.Abort(_(b'cannot unmark copy in merge commit'))
+            # avoid cycle context -> subrepo -> cmdutil
+            from . import context
+
+            rewriteutil.precheck(repo, [ctx.rev()], b'uncopy')
+            new_ctx = context.overlayworkingctx(repo)
+            new_ctx.setbase(ctx.p1())
+            mergemod.graft(repo, ctx, wctx=new_ctx)
+
+        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:
-                wctx[f].markcopied(None)
+                new_ctx[f].markcopied(None)
             elif match.exact(f):
                 ui.warn(
                     _(
@@ -1442,8 +1461,25 @@
                     )
                     % uipathfn(f)
                 )
+
+        if ctx.rev() is not None:
+            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'uncopy', fixphase=True
+                )
+
         return
 
+    if opts.get(b'rev'):
+        raise error.Abort(_("--rev is only supported with --forget"))
+
     def walkpat(pat):
         srcs = []
         m = scmutil.match(wctx, [pat], opts, globbed=True)



To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: marmoute, pulkit, durin42, mercurial-devel
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

D8030: copy: add support for unmarking committed copies

marmoute (Pierre-Yves David)
In reply to this post by marmoute (Pierre-Yves David)
martinvonz updated this revision to Diff 20200.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D8030?vs=20192&id=20200

BRANCH
  default

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

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

AFFECTED FILES
  mercurial/cmdutil.py
  mercurial/commands.py
  mercurial/context.py
  relnotes/next
  tests/test-completion.t
  tests/test-copy.t
  tests/test-rename-after-merge.t

CHANGE DETAILS

diff --git a/tests/test-rename-after-merge.t b/tests/test-rename-after-merge.t
--- a/tests/test-rename-after-merge.t
+++ b/tests/test-rename-after-merge.t
@@ -120,4 +120,10 @@
   $ hg log -r tip -C -v | grep copies
   copies:      b2 (b1)
 
+Test unmarking copies in merge commit
+
+  $ hg copy --forget -r . b2
+  abort: cannot unmark copy in merge commit
+  [255]
+
   $ cd ..
diff --git a/tests/test-copy.t b/tests/test-copy.t
--- a/tests/test-copy.t
+++ b/tests/test-copy.t
@@ -319,5 +319,56 @@
   A dir2/bar
   A dir2/foo
   ? dir2/untracked
+# Clean up for next test
+  $ hg forget dir2
+  removing dir2/bar
+  removing dir2/foo
+  $ rm -r dir2
+
+Test uncopy on committed copies
+
+# Commit some copies
+  $ hg cp bar baz
+  $ hg cp bar qux
+  $ hg ci -m copies
+  $ hg st -C --change .
+  A baz
+    bar
+  A qux
+    bar
+  $ base=$(hg log -r '.^' -T '{rev}')
+  $ hg log -G -T '{rev}:{node|short} {desc}\n' -r $base:
+  @  5:a612dc2edfda copies
+  |
+  o  4:4800b1f1f38e add dir/
+  |
+  ~
+# Add a dirty change on top to show that it's unaffected
+  $ echo dirty >> baz
+  $ hg st
+  M baz
+  $ cat baz
+  bleah
+  dirty
+  $ hg copy --forget -r . baz
+  saved backup bundle to $TESTTMP/part2/.hg/strip-backup/a612dc2edfda-e36b4448-uncopy.hg
+# The unwanted copy is no longer recorded, but the unrelated one is
+  $ hg st -C --change .
+  A baz
+  A qux
+    bar
+# The old commit is gone and we have updated to the new commit
+  $ hg log -G -T '{rev}:{node|short} {desc}\n' -r $base:
+  @  5:c45090e5effe copies
+  |
+  o  4:4800b1f1f38e add dir/
+  |
+  ~
+# Working copy still has the uncommitted change
+  $ hg st
+  M baz
+  $ cat baz
+  bleah
+  dirty
 
   $ cd ..
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -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: forget, after, force, include, exclude, dry-run
+  copy: forget, after, rev, force, include, exclude, dry-run
   debugancestor:
   debugapplystreamclonebundle:
   debugbuilddag: mergeable-file, overwritten-file, new-file
diff --git a/relnotes/next b/relnotes/next
--- a/relnotes/next
+++ b/relnotes/next
@@ -12,7 +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.
+ * `hg copy --forget` can be used to unmark a file as copied. Use `hg
+   copy --forget -r REV` to unmark already committed copies.
 
 
 == New Experimental Features ==
diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -2487,6 +2487,17 @@
             editor=editor,
         )
 
+    def tomemctx_for_amend(self, precursor):
+        extra = precursor.extra().copy()
+        extra[b'amend_source'] = precursor.hex()
+        return self.tomemctx(
+            text=precursor.description(),
+            branch=precursor.branch(),
+            extra=extra,
+            date=precursor.date(),
+            user=precursor.user(),
+        )
+
     def isdirty(self, path):
         return path in self._cache
 
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -2312,6 +2312,13 @@
         (b'', b'forget', None, _(b'unmark a file as copied')),
         (b'A', b'after', None, _(b'record a copy that has already occurred')),
         (
+            b'r',
+            b'rev',
+            b'',
+            _(b'unmark copies in the given revision'),
+            _(b'REV'),
+        ),
+        (
             b'f',
             b'force',
             None,
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -1427,14 +1427,33 @@
     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):
+        rev = opts[b'rev']
+        if rev:
+            ctx = scmutil.revsingle(repo, rev)
+        else:
+            ctx = repo[None]
+        if ctx.rev() is None:
+            new_ctx = ctx
+        else:
+            if len(ctx.parents()) > 1:
+                raise error.Abort(_(b'cannot unmark copy in merge commit'))
+            # avoid cycle context -> subrepo -> cmdutil
+            from . import context
+
+            rewriteutil.precheck(repo, [ctx.rev()], b'uncopy')
+            new_ctx = context.overlayworkingctx(repo)
+            new_ctx.setbase(ctx.p1())
+            mergemod.graft(repo, ctx, wctx=new_ctx)
+
+        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:
-                wctx[f].markcopied(None)
+                new_ctx[f].markcopied(None)
             elif match.exact(f):
                 ui.warn(
                     _(
@@ -1442,8 +1461,25 @@
                     )
                     % uipathfn(f)
                 )
+
+        if ctx.rev() is not None:
+            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'uncopy', fixphase=True
+                )
+
         return
 
+    if opts.get(b'rev'):
+        raise error.Abort(_("--rev is only supported with --forget"))
+
     def walkpat(pat):
         srcs = []
         m = scmutil.match(ctx, [pat], opts, globbed=True)



To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: marmoute, pulkit, durin42, mercurial-devel
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

D8030: copy: add support for unmarking committed copies

marmoute (Pierre-Yves David)
In reply to this post by marmoute (Pierre-Yves David)
martinvonz updated this revision to Diff 20205.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D8030?vs=20200&id=20205

BRANCH
  default

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

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

AFFECTED FILES
  mercurial/cmdutil.py
  mercurial/commands.py
  mercurial/context.py
  relnotes/next
  tests/test-completion.t
  tests/test-copy.t
  tests/test-rename-after-merge.t

CHANGE DETAILS

diff --git a/tests/test-rename-after-merge.t b/tests/test-rename-after-merge.t
--- a/tests/test-rename-after-merge.t
+++ b/tests/test-rename-after-merge.t
@@ -120,4 +120,10 @@
   $ hg log -r tip -C -v | grep copies
   copies:      b2 (b1)
 
+Test unmarking copies in merge commit
+
+  $ hg copy --forget -r . b2
+  abort: cannot unmark copy in merge commit
+  [255]
+
   $ cd ..
diff --git a/tests/test-copy.t b/tests/test-copy.t
--- a/tests/test-copy.t
+++ b/tests/test-copy.t
@@ -319,5 +319,56 @@
   A dir2/bar
   A dir2/foo
   ? dir2/untracked
+# Clean up for next test
+  $ hg forget dir2
+  removing dir2/bar
+  removing dir2/foo
+  $ rm -r dir2
+
+Test uncopy on committed copies
+
+# Commit some copies
+  $ hg cp bar baz
+  $ hg cp bar qux
+  $ hg ci -m copies
+  $ hg st -C --change .
+  A baz
+    bar
+  A qux
+    bar
+  $ base=$(hg log -r '.^' -T '{rev}')
+  $ hg log -G -T '{rev}:{node|short} {desc}\n' -r $base:
+  @  5:a612dc2edfda copies
+  |
+  o  4:4800b1f1f38e add dir/
+  |
+  ~
+# Add a dirty change on top to show that it's unaffected
+  $ echo dirty >> baz
+  $ hg st
+  M baz
+  $ cat baz
+  bleah
+  dirty
+  $ hg copy --forget -r . baz
+  saved backup bundle to $TESTTMP/part2/.hg/strip-backup/a612dc2edfda-e36b4448-uncopy.hg
+# The unwanted copy is no longer recorded, but the unrelated one is
+  $ hg st -C --change .
+  A baz
+  A qux
+    bar
+# The old commit is gone and we have updated to the new commit
+  $ hg log -G -T '{rev}:{node|short} {desc}\n' -r $base:
+  @  5:c45090e5effe copies
+  |
+  o  4:4800b1f1f38e add dir/
+  |
+  ~
+# Working copy still has the uncommitted change
+  $ hg st
+  M baz
+  $ cat baz
+  bleah
+  dirty
 
   $ cd ..
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -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: forget, after, force, include, exclude, dry-run
+  copy: forget, after, rev, force, include, exclude, dry-run
   debugancestor:
   debugapplystreamclonebundle:
   debugbuilddag: mergeable-file, overwritten-file, new-file
diff --git a/relnotes/next b/relnotes/next
--- a/relnotes/next
+++ b/relnotes/next
@@ -12,7 +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.
+ * `hg copy --forget` can be used to unmark a file as copied. Use `hg
+   copy --forget -r REV` to unmark already committed copies.
 
 
 == New Experimental Features ==
diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -2487,6 +2487,17 @@
             editor=editor,
         )
 
+    def tomemctx_for_amend(self, precursor):
+        extra = precursor.extra().copy()
+        extra[b'amend_source'] = precursor.hex()
+        return self.tomemctx(
+            text=precursor.description(),
+            branch=precursor.branch(),
+            extra=extra,
+            date=precursor.date(),
+            user=precursor.user(),
+        )
+
     def isdirty(self, path):
         return path in self._cache
 
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -2312,6 +2312,13 @@
         (b'', b'forget', None, _(b'unmark a file as copied')),
         (b'A', b'after', None, _(b'record a copy that has already occurred')),
         (
+            b'r',
+            b'rev',
+            b'',
+            _(b'unmark copies in the given revision'),
+            _(b'REV'),
+        ),
+        (
             b'f',
             b'force',
             None,
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -1427,14 +1427,33 @@
     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):
+        rev = opts[b'rev']
+        if rev:
+            ctx = scmutil.revsingle(repo, rev)
+        else:
+            ctx = repo[None]
+        if ctx.rev() is None:
+            new_ctx = ctx
+        else:
+            if len(ctx.parents()) > 1:
+                raise error.Abort(_(b'cannot unmark copy in merge commit'))
+            # avoid cycle context -> subrepo -> cmdutil
+            from . import context
+
+            rewriteutil.precheck(repo, [ctx.rev()], b'uncopy')
+            new_ctx = context.overlayworkingctx(repo)
+            new_ctx.setbase(ctx.p1())
+            mergemod.graft(repo, ctx, wctx=new_ctx)
+
+        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:
-                wctx[f].markcopied(None)
+                new_ctx[f].markcopied(None)
             elif match.exact(f):
                 ui.warn(
                     _(
@@ -1442,8 +1461,25 @@
                     )
                     % uipathfn(f)
                 )
+
+        if ctx.rev() is not None:
+            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'uncopy', fixphase=True
+                )
+
         return
 
+    if opts.get(b'rev'):
+        raise error.Abort(_("--rev is only supported with --forget"))
+
     def walkpat(pat):
         srcs = []
         m = scmutil.match(ctx, [pat], opts, globbed=True)



To: martinvonz, #hg-reviewers, durin42, marmoute
Cc: marmoute, pulkit, durin42, mercurial-devel
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
12