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 diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -2315,7 +2315,7 @@ b'r', b'rev', b'', - _(b'unmark copies in the given revision (EXPERIMENTAL)'), + _(b'(un)mark copies in the given revision (EXPERIMENTAL)'), _(b'REV'), ), ( @@ -2345,7 +2345,7 @@ all given (positional) arguments are unmarked as copies. The destination file(s) will be left in place (still tracked). - This command takes effect with the next commit. + This command takes effect with the next commit by default. Returns 0 on success, 1 if errors are encountered. """ diff --git a/relnotes/next b/relnotes/next --- a/relnotes/next +++ b/relnotes/next @@ -14,8 +14,11 @@ * `hg copy --forget` can be used to unmark a file as copied. +== New Experimental Features == -== New Experimental Features == + * `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). * Use `hg copy --forget -r REV` to unmark already committed copies. 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-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