What does hg copy do?

10,063

Solution 1

  • What exactly does hg copy do and what special treatment does this cause in the future?

It adds new files and marks them as copies of the old files. Because they are copies, a change made in the original file will be merged into copy. Time flows from left to right:

(init) --- (edit a.txt) ---- (a.txt edit is copied to b.txt)
      \                     /
       (hg copy a.txt b.txt)
  • If it turns out to do 'the wrong thing(tm)' for our case, how do I unflag the file as beeing a copy of another file?

This mechanism only kicks in when you merge. If b.txt is not present in the common ancestor revision (init in the above graph), then Mercurial will do a search backwards to see if b.txt is copied from somewhere else.

Let us continue the above graph in abbreviated form:

(i) -- (edit a) -- (a edit copied to b) -- (edit a) -- (merge)
   \              /                                   /
    (copy a b) --/------- (edit b) ------------------/

The question is how the final merge is done. The common ancestor point is now the copy a b node and here both a and b are present. This means that there wont be any search for copies! So the second edit to a wont be merged into b.

To double-check, I tried it out:

$ hg init
$ echo a > a
$ hg add a
$ hg commit -m init
$ hg copy a b
$ hg commit -m "copy a b"

This was the copy, b now contains a only.

$ hg update 0
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
$ echo aa >> a
$ hg commit -m "edit a"
created a new head
$ hg merge
merging a and b to b
0 files updated, 1 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
$ hg commit -m "a edit copied to b"

This was the first merge and the edit to a has been copied into b:

$ cat b
a
aa

We now make changes in parallel:

$ echo aaa >> a
$ hg commit -m "edit a again"
$ hg update 3
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ echo bbb >> b
$ hg commit -m "edit b"
created new head
$ hg merge
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)

There are no further copying done:

$ cat a
a
aa
aaa
$ cat b
a
aa
bbb

As for disabling this... you can't really explicitly disable the copy detection. But as I hope to have illustrated above, it wont "bother" you again after the first merge.

If the first merge is a problem, then you can use hg resolve --tool internal:local to reset the files back to their state before you started the merge. So with

$ hg resolve --tool internal:local b

we could have brought b back to just containing one line with a.

Solution 2

How do I unflag the file as being a copy of another file?

If you revert a hg copy, the copied-to file remains in your working directory afterwards, untracked. You just have to add it normally. The copied-from file isn't affected at all.

% hg copy file new-file
% hg status -C
A new-file
__file
% hg revert new-file
% hg add new-file
% hg status -C
A new-file

Reference: Mercurial: The definitive guide

Share:
10,063
Martin Geisler
Author by

Martin Geisler

Hi! I've studied cryptography at University of Aarhus in Denmark. I now like in Zurich, Switzerland. I mostly work with Python, but also like functional languages like Haskell a lot. Currently, I work mostly with Rust, see my open source projects if you're curious. You can also take a look at my CV if you want to know where I've worked professionally.

Updated on June 01, 2022

Comments

  • Martin Geisler
    Martin Geisler about 2 years

    We recently did a hg copy of a directory in our repository. We thought it does something like cp -a and hg add and maybe flag somehow that this file has been copied from another file inside the repo (so hg annotate shows the original committer). But it now seems that hg copy does more or different stuff than that. I couldn't really find much on how exactly copy works. So:

    • What exactly does hg copy do and what special treatment does this cause in the future?
    • If it turns out to do "the wrong thing(tm)" for our case, how do I unflag the file as beeing a copy of another file?

    (This question was asked on the Mercurial mailinglist, you may want to follow the original thread too.)

  • Lazy Badger
    Lazy Badger over 12 years
    Sorry, can not confirm copy-changes on merge heads in my test. >hg version Mercurial Distributed SCM (version 2.0.1)
  • Martin Geisler
    Martin Geisler over 12 years
    Also not in the first merge? Please write [email protected] so that we can discuss it in full there, that's much better than these small comment boxes :)
  • Lazy Badger
    Lazy Badger over 12 years
    "Also not in the first merge?" - yes. e-mail noted, message will be send (with logs)
  • Martin Geisler
    Martin Geisler over 12 years
    It's strange if you cannot reproduce it with my steps... Thanks for sending the logs to the list, that should make it easier to discuss.
  • Martin Geisler
    Martin Geisler over 12 years
    Did you figure out the problem? I didn't see any logs on the mailing list.
  • Joshua Goldberg
    Joshua Goldberg about 8 years
    I wanted to note: the effect is not to keep a and b "in synch" through the first merge. In the 1st-merge, b and only b winds up the merged result of any changes to a on the b-free branch, along with the changes to b on the branch with the b copy. (It wasn't clear to me reading the answer above whether hg would also merge changes back to a, since the example didn't modify b before the 1st-merge. From a quick test, I believe it does not.)