What does hg copy do?

Martin Geisler mg at lazybytes.net
Fri Dec 16 09:06:15 UTC 2011


Christoph Mathys <eraserix at gmail.com> writes:

> We recently did a 'hg copy' of a directory in our repo. 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?

It adds new files and mark 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 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 that the above is right, I tries 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 futher 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".

-- 
Martin Geisler

Mercurial links: http://mercurial.ch/
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 197 bytes
Desc: not available
URL: <http://lists.mercurial-scm.org/pipermail/mercurial/attachments/20111216/ed61c4d9/attachment.asc>


More information about the Mercurial mailing list