D8035: copy: add support for marking committed copies
martinvonz (Martin von Zweigbergk)
phabricator at mercurial-scm.org
Thu Feb 13 23:25:30 UTC 2020
martinvonz updated this revision to Diff 20206.
REPOSITORY
rHG Mercurial
CHANGES SINCE LAST UPDATE
https://phab.mercurial-scm.org/D8035?vs=20195&id=20206
BRANCH
default
CHANGES SINCE LAST ACTION
https://phab.mercurial-scm.org/D8035/new/
REVISION DETAIL
https://phab.mercurial-scm.org/D8035
AFFECTED FILES
mercurial/cmdutil.py
mercurial/commands.py
relnotes/next
tests/test-copy.t
tests/test-rename-after-merge.t
tests/test-rename-rev.t
tests/test-rename.t
CHANGE DETAILS
diff --git a/tests/test-rename.t b/tests/test-rename-rev.t
copy from tests/test-rename.t
copy to tests/test-rename-rev.t
--- a/tests/test-rename.t
+++ b/tests/test-rename-rev.t
@@ -6,693 +6,51 @@
$ echo d1/b > d1/b
$ echo d2/b > d2/b
$ hg add d1/a d1/b d1/ba d1/d11/a1 d2/b
- $ hg commit -m "1"
-
-rename a single file
-
- $ hg rename d1/d11/a1 d2/c
- $ hg --config ui.portablefilenames=abort rename d1/a d1/con.xml
- abort: filename contains 'con', which is reserved on Windows: d1/con.xml
- [255]
- $ hg sum
- parent: 0:9b4b6e7b2c26 tip
- 1
- branch: default
- commit: 1 renamed
- update: (current)
- phases: 1 draft
- $ hg status -C
- A d2/c
- d1/d11/a1
- R d1/d11/a1
- $ hg update -C
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm d2/c
-
-rename a single file using absolute paths
-
- $ hg rename `pwd`/d1/d11/a1 `pwd`/d2/c
- $ hg status -C
- A d2/c
- d1/d11/a1
- R d1/d11/a1
- $ hg update -C
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm d2/c
+ $ hg commit -m "intial"
-rename --after a single file
- $ mv d1/d11/a1 d2/c
- $ hg rename --after d1/d11/a1 d2/c
- $ hg status -C
- A d2/c
- d1/d11/a1
- R d1/d11/a1
- $ hg update -C
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm d2/c
-
-rename --after a single file when src and tgt already tracked
-
- $ mv d1/d11/a1 d2/c
- $ hg addrem -s 0
- removing d1/d11/a1
- adding d2/c
- $ hg rename --after d1/d11/a1 d2/c
- $ hg status -C
- A d2/c
- d1/d11/a1
- R d1/d11/a1
- $ hg update -C
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm d2/c
-
-rename --after a single file to a nonexistent target filename
-
- $ hg rename --after d1/a dummy
- d1/a: not recording move - dummy does not exist
- [1]
-
-move a single file to an existing directory
+Test single file
- $ hg rename d1/d11/a1 d2
- $ hg status -C
- A d2/a1
- d1/d11/a1
- R d1/d11/a1
- $ hg update -C
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm d2/a1
-
-move --after a single file to an existing directory
-
- $ mv d1/d11/a1 d2
- $ hg rename --after d1/d11/a1 d2
- $ hg status -C
- A d2/a1
- d1/d11/a1
- R d1/d11/a1
- $ hg update -C
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm d2/a1
-
-rename a file using a relative path
-
- $ (cd d1/d11; hg rename ../../d2/b e)
- $ hg status -C
- A d1/d11/e
- d2/b
- R d2/b
- $ hg update -C
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm d1/d11/e
-
-rename --after a file using a relative path
-
- $ (cd d1/d11; mv ../../d2/b e; hg rename --after ../../d2/b e)
- $ hg status -C
- A d1/d11/e
- d2/b
- R d2/b
- $ hg update -C
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm d1/d11/e
-
-rename directory d1 as d3
-
- $ hg rename d1/ d3
- moving d1/a to d3/a
- moving d1/b to d3/b
- moving d1/ba to d3/ba
- moving d1/d11/a1 to d3/d11/a1
- $ hg status -C
- A d3/a
- d1/a
- A d3/b
- d1/b
- A d3/ba
- d1/ba
- A d3/d11/a1
- d1/d11/a1
- R d1/a
- R d1/b
- R d1/ba
- R d1/d11/a1
- $ hg update -C
- 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm -rf d3
-
-rename --after directory d1 as d3
-
- $ mv d1 d3
- $ hg rename --after d1 d3
- moving d1/a to d3/a
- moving d1/b to d3/b
- moving d1/ba to d3/ba
- moving d1/d11/a1 to d3/d11/a1
- $ hg status -C
- A d3/a
- d1/a
- A d3/b
+# One recoded copy, one copy to record after commit
+ $ hg cp d1/b d1/c
+ $ cp d1/b d1/d
+ $ hg add d1/d
+ $ hg ci -m 'copy d1/b to d1/c and d1/d'
+ $ hg st -C --change .
+ A d1/c
d1/b
- A d3/ba
- d1/ba
- A d3/d11/a1
- d1/d11/a1
- R d1/a
- R d1/b
- R d1/ba
- R d1/d11/a1
- $ hg update -C
- 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm -rf d3
-
-move a directory using a relative path
-
- $ (cd d2; mkdir d3; hg rename ../d1/d11 d3)
- moving ../d1/d11/a1 to d3/d11/a1
- $ hg status -C
- A d2/d3/d11/a1
- d1/d11/a1
- R d1/d11/a1
- $ hg update -C
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm -rf d2/d3
-
-move --after a directory using a relative path
-
- $ (cd d2; mkdir d3; mv ../d1/d11 d3; hg rename --after ../d1/d11 d3)
- moving ../d1/d11/a1 to d3/d11/a1
- $ hg status -C
- A d2/d3/d11/a1
- d1/d11/a1
- R d1/d11/a1
- $ hg update -C
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm -rf d2/d3
-
-move directory d1/d11 to an existing directory d2 (removes empty d1)
-
- $ hg rename d1/d11/ d2
- moving d1/d11/a1 to d2/d11/a1
- $ hg status -C
- A d2/d11/a1
- d1/d11/a1
- R d1/d11/a1
- $ hg update -C
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm -rf d2/d11
-
-move directories d1 and d2 to a new directory d3
-
- $ mkdir d3
- $ hg rename d1 d2 d3
- moving d1/a to d3/d1/a
- moving d1/b to d3/d1/b
- moving d1/ba to d3/d1/ba
- moving d1/d11/a1 to d3/d1/d11/a1
- moving d2/b to d3/d2/b
- $ hg status -C
- A d3/d1/a
- d1/a
- A d3/d1/b
+ A d1/d
+# Errors out without --after for now
+ $ hg cp -r . d1/b d1/d
+ abort: --rev requires --after
+ [255]
+# Errors out with non-existent destination
+ $ hg cp -A -r . d1/b d1/non-existent
+ abort: d1/non-existent: copy destination does not exist in 8a9d70fa20c9
+ [255]
+# Successful invocation
+ $ hg cp -A -r . d1/b d1/d
+ saved backup bundle to $TESTTMP/.hg/strip-backup/8a9d70fa20c9-973ae357-copy.hg
+# New copy is recorded, and previously recorded copy is also still there
+ $ hg st -C --change .
+ A d1/c
d1/b
- A d3/d1/ba
- d1/ba
- A d3/d1/d11/a1
- d1/d11/a1
- A d3/d2/b
- d2/b
- R d1/a
- R d1/b
- R d1/ba
- R d1/d11/a1
- R d2/b
- $ hg update -C
- 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm -rf d3
-
-move --after directories d1 and d2 to a new directory d3
-
- $ mkdir d3
- $ mv d1 d2 d3
- $ hg rename --after d1 d2 d3
- moving d1/a to d3/d1/a
- moving d1/b to d3/d1/b
- moving d1/ba to d3/d1/ba
- moving d1/d11/a1 to d3/d1/d11/a1
- moving d2/b to d3/d2/b
- $ hg status -C
- A d3/d1/a
- d1/a
- A d3/d1/b
+ A d1/d
d1/b
- A d3/d1/ba
- d1/ba
- A d3/d1/d11/a1
- d1/d11/a1
- A d3/d2/b
- d2/b
- R d1/a
- R d1/b
- R d1/ba
- R d1/d11/a1
- R d2/b
- $ hg update -C
- 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm -rf d3
-move everything under directory d1 to existing directory d2, do not
-overwrite existing files (d2/b)
+Test using directory as destination
- $ hg rename d1/* d2
- d2/b: not overwriting - file already committed
- ('hg rename --force' to replace the file by recording a rename)
- moving d1/d11/a1 to d2/d11/a1
- [1]
- $ hg status -C
- A d2/a
- d1/a
- A d2/ba
- d1/ba
- A d2/d11/a1
- d1/d11/a1
- R d1/a
- R d1/ba
- R d1/d11/a1
- $ diff -u d1/b d2/b
- --- d1/b * (glob)
- +++ d2/b * (glob)
- @@ * (glob)
- -d1/b
- +d2/b
- [1]
- $ hg update -C
- 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm d2/a d2/ba d2/d11/a1
-
-attempt to move one file into a non-existent directory
-
- $ hg rename d1/a dx/
- abort: destination dx/ is not a directory
- [255]
- $ hg status -C
- $ hg update -C
- 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-
-attempt to move potentially more than one file into a non-existent directory
-
- $ hg rename 'glob:d1/**' dx
- abort: with multiple sources, destination must be an existing directory
+ $ hg co 0
+ 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ $ cp -R d1 d3
+ $ hg add d3
+ adding d3/a
+ adding d3/b
+ adding d3/ba
+ adding d3/d11/a1
+ $ hg ci -m 'copy d1/ to d3/'
+ created new head
+ $ hg cp -A -r . d1 d3
+ abort: d3: --rev does not support a directory as destination
[255]
-move every file under d1 to d2/d21
-
- $ mkdir d2/d21
- $ hg rename 'glob:d1/**' d2/d21
- moving d1/a to d2/d21/a
- moving d1/b to d2/d21/b
- moving d1/ba to d2/d21/ba
- moving d1/d11/a1 to d2/d21/a1
- $ hg status -C
- A d2/d21/a
- d1/a
- A d2/d21/a1
- d1/d11/a1
- A d2/d21/b
- d1/b
- A d2/d21/ba
- d1/ba
- R d1/a
- R d1/b
- R d1/ba
- R d1/d11/a1
- $ hg update -C
- 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm -rf d2/d21
-
-move --after some files under d1 to d2/d21
-
- $ mkdir d2/d21
- $ mv d1/a d1/d11/a1 d2/d21
- $ hg rename --after 'glob:d1/**' d2/d21
- moving d1/a to d2/d21/a
- d1/b: not recording move - d2/d21/b does not exist
- d1/ba: not recording move - d2/d21/ba does not exist
- moving d1/d11/a1 to d2/d21/a1
- [1]
- $ hg status -C
- A d2/d21/a
- d1/a
- A d2/d21/a1
- d1/d11/a1
- R d1/a
- R d1/d11/a1
- $ hg update -C
- 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm -rf d2/d21
-
-move every file under d1 starting with an 'a' to d2/d21 (regexp)
-
- $ mkdir d2/d21
- $ hg rename 're:d1/([^a][^/]*/)*a.*' d2/d21
- moving d1/a to d2/d21/a
- moving d1/d11/a1 to d2/d21/a1
- $ hg status -C
- A d2/d21/a
- d1/a
- A d2/d21/a1
- d1/d11/a1
- R d1/a
- R d1/d11/a1
- $ hg update -C
- 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm -rf d2/d21
-
-attempt to overwrite an existing file
-
- $ echo "ca" > d1/ca
- $ hg rename d1/ba d1/ca
- d1/ca: not overwriting - file exists
- ('hg rename --after' to record the rename)
- [1]
- $ hg status -C
- ? d1/ca
- $ hg update -C
- 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-
-forced overwrite of an existing file
-
- $ echo "ca" > d1/ca
- $ hg rename --force d1/ba d1/ca
- $ hg status -C
- A d1/ca
- d1/ba
- R d1/ba
- $ hg update -C
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm d1/ca
-
-attempt to overwrite an existing broken symlink
-
-#if symlink
- $ ln -s ba d1/ca
- $ hg rename --traceback d1/ba d1/ca
- d1/ca: not overwriting - file exists
- ('hg rename --after' to record the rename)
- [1]
- $ hg status -C
- ? d1/ca
- $ hg update -C
- 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm d1/ca
-
-replace a symlink with a file
-
- $ ln -s ba d1/ca
- $ hg rename --force d1/ba d1/ca
- $ hg status -C
- A d1/ca
- d1/ba
- R d1/ba
- $ hg update -C
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm d1/ca
-#endif
-
-do not copy more than one source file to the same destination file
-
- $ mkdir d3
- $ hg rename d1/* d2/* d3
- moving d1/d11/a1 to d3/d11/a1
- d3/b: not overwriting - d2/b collides with d1/b
- [1]
- $ hg status -C
- A d3/a
- d1/a
- A d3/b
- d1/b
- A d3/ba
- d1/ba
- A d3/d11/a1
- d1/d11/a1
- R d1/a
- R d1/b
- R d1/ba
- R d1/d11/a1
- $ hg update -C
- 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm -rf d3
-
-move a whole subtree with "hg rename ."
-
- $ mkdir d3
- $ (cd d1; hg rename . ../d3)
- moving a to ../d3/d1/a
- moving b to ../d3/d1/b
- moving ba to ../d3/d1/ba
- moving d11/a1 to ../d3/d1/d11/a1
- $ hg status -C
- A d3/d1/a
- d1/a
- A d3/d1/b
- d1/b
- A d3/d1/ba
- d1/ba
- A d3/d1/d11/a1
- d1/d11/a1
- R d1/a
- R d1/b
- R d1/ba
- R d1/d11/a1
- $ hg update -C
- 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm -rf d3
-
-move a whole subtree with "hg rename --after ."
-
- $ mkdir d3
- $ mv d1/* d3
- $ (cd d1; hg rename --after . ../d3)
- moving a to ../d3/a
- moving b to ../d3/b
- moving ba to ../d3/ba
- moving d11/a1 to ../d3/d11/a1
- $ hg status -C
- A d3/a
- d1/a
- A d3/b
- d1/b
- A d3/ba
- d1/ba
- A d3/d11/a1
- d1/d11/a1
- R d1/a
- R d1/b
- R d1/ba
- R d1/d11/a1
- $ hg update -C
- 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm -rf d3
-
-move the parent tree with "hg rename .."
-
- $ (cd d1/d11; hg rename .. ../../d3)
- moving ../a to ../../d3/a
- moving ../b to ../../d3/b
- moving ../ba to ../../d3/ba
- moving a1 to ../../d3/d11/a1
- $ hg status -C
- A d3/a
- d1/a
- A d3/b
- d1/b
- A d3/ba
- d1/ba
- A d3/d11/a1
- d1/d11/a1
- R d1/a
- R d1/b
- R d1/ba
- R d1/d11/a1
- $ hg update -C
- 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm -rf d3
-
-skip removed files
-
- $ hg remove d1/b
- $ hg rename d1 d3
- moving d1/a to d3/a
- moving d1/ba to d3/ba
- moving d1/d11/a1 to d3/d11/a1
- $ hg status -C
- A d3/a
- d1/a
- A d3/ba
- d1/ba
- A d3/d11/a1
- d1/d11/a1
- R d1/a
- R d1/b
- R d1/ba
- R d1/d11/a1
- $ hg update -C
- 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm -rf d3
-
-transitive rename
-
- $ hg rename d1/b d1/bb
- $ hg rename d1/bb d1/bc
- $ hg status -C
- A d1/bc
- d1/b
- R d1/b
- $ hg update -C
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm d1/bc
-
-transitive rename --after
-
- $ hg rename d1/b d1/bb
- $ mv d1/bb d1/bc
- $ hg rename --after d1/bb d1/bc
- $ hg status -C
- A d1/bc
- d1/b
- R d1/b
- $ hg update -C
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm d1/bc
-
- $ echo "# idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)"
- # idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)
- $ hg rename d1/b d1/bb
- $ echo "some stuff added to d1/bb" >> d1/bb
- $ hg rename d1/bb d1/b
- $ hg status -C
- M d1/b
- $ hg update -C
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-
-overwriting with renames (issue1959)
-
- $ hg rename d1/a d1/c
- $ hg rename d1/b d1/a
- $ hg status -C
- M d1/a
- d1/b
- A d1/c
- d1/a
- R d1/b
- $ hg diff --git
- diff --git a/d1/a b/d1/a
- --- a/d1/a
- +++ b/d1/a
- @@ -1,1 +1,1 @@
- -d1/a
- +d1/b
- diff --git a/d1/b b/d1/b
- deleted file mode 100644
- --- a/d1/b
- +++ /dev/null
- @@ -1,1 +0,0 @@
- -d1/b
- diff --git a/d1/a b/d1/c
- copy from d1/a
- copy to d1/c
- $ hg update -C
- 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm d1/c # The file was marked as added, so 'hg update' action was 'forget'
-
-check illegal path components
-
- $ hg rename d1/d11/a1 .hg/foo
- abort: path contains illegal component: .hg/foo
- [255]
- $ hg status -C
- $ hg rename d1/d11/a1 ../foo
- abort: ../foo not under root '$TESTTMP'
- [255]
- $ hg status -C
-
- $ mv d1/d11/a1 .hg/foo
- $ hg rename --after d1/d11/a1 .hg/foo
- abort: path contains illegal component: .hg/foo
- [255]
- $ hg status -C
- ! d1/d11/a1
- $ hg update -C
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm .hg/foo
-
- $ hg rename d1/d11/a1 .hg
- abort: path contains illegal component: .hg/a1
- [255]
- $ hg --config extensions.largefiles= rename d1/d11/a1 .hg
- The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
- abort: path contains illegal component: .hg/a1
- [255]
- $ hg status -C
- $ hg rename d1/d11/a1 ..
- abort: ../a1 not under root '$TESTTMP'
- [255]
- $ hg --config extensions.largefiles= rename d1/d11/a1 ..
- The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
- abort: ../a1 not under root '$TESTTMP'
- [255]
- $ hg status -C
-
- $ mv d1/d11/a1 .hg
- $ hg rename --after d1/d11/a1 .hg
- abort: path contains illegal component: .hg/a1
- [255]
- $ hg status -C
- ! d1/d11/a1
- $ hg update -C
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ rm .hg/a1
-
- $ (cd d1/d11; hg rename ../../d2/b ../../.hg/foo)
- abort: path contains illegal component: .hg/foo
- [255]
- $ hg status -C
- $ (cd d1/d11; hg rename ../../d2/b ../../../foo)
- abort: ../../../foo not under root '$TESTTMP'
- [255]
- $ hg status -C
-
-check that stat information such as mtime is preserved on rename - it's unclear
-whether the `touch` and `stat` commands are portable, so we mimic them using
-python. Not all platforms support precision of even one-second granularity, so
-we allow a rather generous fudge factor here; 1234567890 is 2009, and the
-primary thing we care about is that it's not the machine's current time;
-hopefully it's really unlikely for a machine to have such a broken clock that
-this test fails. :)
-
- $ mkdir mtime
-Create the file (as empty), then update its mtime and atime to be 1234567890.
- >>> import os
- >>> filename = "mtime/f"
- >>> mtime = 1234567890
- >>> open(filename, "w").close()
- >>> os.utime(filename, (mtime, mtime))
- $ hg ci -qAm 'add mtime dir'
-"hg cp" does not preserve the mtime, so it should be newer than the 2009
-timestamp.
- $ hg cp -q mtime mtime_cp
- >>> from __future__ import print_function
- >>> import os
- >>> filename = "mtime_cp/f"
- >>> print(os.stat(filename).st_mtime < 1234567999)
- False
-"hg mv" preserves the mtime, so it should be ~equal to the 2009 timestamp
-(modulo some fudge factor due to not every system supporting 1s-level
-precision).
- $ hg mv -q mtime mtime_mv
- >>> from __future__ import print_function
- >>> import os
- >>> filename = "mtime_mv/f"
- >>> print(os.stat(filename).st_mtime < 1234567999)
- True
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,10 +120,14 @@
$ hg log -r tip -C -v | grep copies
copies: b2 (b1)
-Test unmarking copies in merge commit
+Test marking/unmarking copies in merge commit
$ hg copy --forget -r . b2
- abort: cannot unmark copy in merge commit
+ abort: cannot mark/unmark copy in merge commit
+ [255]
+
+ $ hg copy --after -r . b1 b2
+ abort: cannot mark/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
@@ -271,7 +271,7 @@
# Set up by creating a copy
$ hg cp bar baz
# Test uncopying a non-existent file
- $ hg copy --forget non-existent
+ $ hg copy --forget non-existent --trace
non-existent: $ENOENT$
# Test uncopying an tracked but unrelated file
$ hg copy --forget foo
diff --git a/relnotes/next b/relnotes/next
--- a/relnotes/next
+++ b/relnotes/next
@@ -12,6 +12,10 @@
commits that are being merged, when there are conflicts. Also works
for conflicts caused by e.g. `hg graft`.
+ * `hg copy` now supports a `--rev` argument to mark files as copied in the
+ specified commit. It only works with `--after` for now (i.e., it's only
+ useful for marking files copied using non-hg `cp` as copied).
+
* `hg copy --forget` can be used to unmark a file as copied. Use `hg
copy --forget -r REV` to unmark already committed copies.
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -2345,7 +2345,8 @@
all given (positional) arguments are unmarked as copies. The destination
file(s) will be left in place (still tracked).
- This command takes effect with the next commit.
+ This command takes effect with the next commit by default. Use
+ -r to (un)mark 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
@@ -1421,17 +1421,23 @@
forget = opts.get(b"forget")
after = opts.get(b"after")
dryrun = opts.get(b"dry_run")
- ctx = repo[None]
+ rev = opts.get(b'rev')
+ if rev:
+ if not forget and not after:
+ # TODO: Remove this restriction and make it also create the copy
+ # targets (and remove the rename source if rename==True).
+ raise error.Abort(_(b'--rev requires --after'))
+ ctx = scmutil.revsingle(repo, rev)
+ if len(ctx.parents()) > 1:
+ raise error.Abort(_(b'cannot mark/unmark copy in merge commit'))
+ else:
+ ctx = repo[None]
+
pctx = ctx.p1()
uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
if forget:
- rev = opts[b'rev']
- if rev:
- ctx = scmutil.revsingle(repo, rev)
- else:
- ctx = repo[None]
if ctx.rev() is None:
new_ctx = ctx
else:
@@ -1484,9 +1490,6 @@
raise error.Abort(_(b'no destination specified'))
dest = pats.pop()
- if opts.get(b'rev'):
- raise error.Abort(_("--rev is only supported with --forget"))
-
def walkpat(pat):
srcs = []
m = scmutil.match(ctx, [pat], opts, globbed=True)
@@ -1517,6 +1520,55 @@
srcs.append((abs, rel, exact))
return srcs
+ if ctx.rev() is not None:
+ rewriteutil.precheck(repo, [ctx.rev()], b'uncopy')
+ absdest = pathutil.canonpath(repo.root, cwd, dest)
+ if ctx.hasdir(absdest):
+ raise error.Abort(
+ _(b'%s: --rev does not support a directory as destination')
+ % uipathfn(absdest)
+ )
+ if absdest not in ctx:
+ raise error.Abort(
+ _(b'%s: copy destination does not exist in %s')
+ % (uipathfn(absdest), ctx)
+ )
+
+ # avoid cycle context -> subrepo -> cmdutil
+ from . import context
+
+ copylist = []
+ for pat in pats:
+ srcs = walkpat(pat)
+ if not srcs:
+ continue
+ for abs, rel, exact in srcs:
+ copylist.append(abs)
+
+ # TODO: Add support for `hg cp -r . foo bar dir` and
+ # `hg cp -r . dir1 dir2`, preferably unifying the code with the existing
+ # functions below.
+ if len(copylist) != 1:
+ raise error.Abort(_(b'--rev requires a single source'))
+
+ new_ctx = context.overlayworkingctx(repo)
+ new_ctx.setbase(ctx.p1())
+ mergemod.graft(repo, ctx, wctx=new_ctx)
+
+ new_ctx.markcopied(absdest, copylist[0])
+
+ with repo.lock():
+ mem_ctx = new_ctx.tomemctx_for_amend(ctx)
+ new_node = mem_ctx.commit()
+
+ if repo.dirstate.p1() == ctx.node():
+ with repo.dirstate.parentchange():
+ scmutil.movedirstate(repo, repo[new_node])
+ replacements = {ctx.node(): [new_node]}
+ scmutil.cleanupnodes(repo, replacements, b'copy', fixphase=True)
+
+ return
+
# abssrc: hgsep
# relsrc: ossep
# otarget: ossep
To: martinvonz, #hg-reviewers, durin42
Cc: durin42, marmoute, mjpieters, mercurial-devel
More information about the Mercurial-devel
mailing list