Bug 4458 - revert fails to restore file that are "removed and then recreated by a move"
Summary: revert fails to restore file that are "removed and then recreated by a move"
Status: RESOLVED FIXED
Alias: None
Product: Mercurial
Classification: Unclassified
Component: Mercurial (show other bugs)
Version: 3.2.1
Hardware: All All
: urgent bug
Assignee: Bugzilla
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-11-18 13:57 UTC by Faheem Mitha
Modified: 2017-12-15 00:00 UTC (History)
4 users (show)

See Also:
Python Version: ---


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Faheem Mitha 2014-11-18 13:57 UTC
The following script causes weirdness. Possibly another manifestation of the infamous linkrev bug.

hg init mq-test
hg init --mq mq-test
cd mq-test
echo foo1 > foo1
hg add foo1
hg qnew test
hg ci --mq -m "add foo1"
echo foo2 > foo2
hg add foo2
hg qref
hg ci --mq -m "add foo2"
hg rm foo1
hg mv foo2 foo1
hg forget foo2
hg revert foo2
hg revert foo1

faheem@orwell:~/mqtestnew/mq-test$ hg st foo1
? foo1
faheem@orwell:~/mqtestnew/mq-test$ hg log foo1
changeset:   0:d05e48dbd1d2
tag:         qbase
tag:         qtip
tag:         test
tag:         tip
user:        Faheem Mitha <faheem@faheem.info>
date:        Tue Nov 18 23:46:28 2014 +0530
summary:     [mq]: test

faheem@orwell:~/mqtestnew/mq-test$ cat .hg/patches/test 
# HG changeset patch
# Parent  0000000000000000000000000000000000000000

diff -r 000000000000 foo1
--- /dev/null
+++ b/foo1
@@ -0,0 +1,1 @@
+foo1
diff -r 000000000000 foo2
--- /dev/null
+++ b/foo2
@@ -0,0 +1,1 @@
+foo2

faheem@orwell:~/mqtestnew/mq-test$ cat foo1
foo2
faheem@orwell:~/mqtestnew/mq-test$ cat foo2
foo2
Comment 1 Matt Mackall 2014-11-19 11:29 UTC
What specifically do you think is weird here?
Comment 2 Matt Mackall 2014-11-19 11:49 UTC
I suspect this is your bug:

---
hg init a
cd a

touch a b
hg ci -Am0

hg rm a
hg mv b a
hg revert a
hg st            # shows a as unknown!
---

..in which case this is a regression from 3.1. Bisect sez:

The first bad revision is:
changeset:   23506:d3659b3795e9
parent:      23419:8dda6f6ff564
user:        Pierre-Yves David <pierre-yves.david@fb.com>
date:        Sat Aug 02 11:32:24 2014 -0700
summary:     revert: simplify handling of `added` files

(adding Pierre-Yves as well as Martin, who's been playing with test cases.)
Comment 3 Pierre-Yves David 2014-11-21 20:54 UTC
fun fact: "rm; move" give different result from "move --force"
Comment 4 Pierre-Yves David 2014-11-24 15:34 UTC
Extensive script displaying behavior around this issue:

  $ hg init repo
  $ cd repo

  $ echo a > a
  $ echo b > b
  $ hg ci -Am0
  adding a
  adding b

  $ cd ..
  $ cp -r repo repo1
  $ cp -r repo repo2
  $ cp -r repo repo3
  $ cp -r repo repo4


Status output
=============

Case 1

The file is explicitly removed, then moved over with another file.

Status initially reports it as "added" with proper rename
information. But after commit the file is seen as modified
(without rename data)

  $ cd repo1
  $ hg rm a
  $ hg st --copies
  R a
  $ hg mv b a
  $ hg st --copies
  A a
    b
  R b
  $ hg commit -m 'blah'
  $ hg st --copies --change .
  M a
  R b
  $ cd ..

Case 2

The file is directly  moved over with another file.

Status initially reports it as "Modified" with proper rename
information. But after commit rename data are no longer
reported.

  $ cd repo2
  $ hg mv --force b a
  $ hg st --copies
  M a
    b
  R b
  $ hg commit -m 'blah'
  $ hg st --copies --change .
  M a
  R b
  $ cd ..

Case 3

The file is explicitly removed in a changesets. Then we moved over with
another file.

Status reports it as "added" with proper rename
information. Even after commit.

  $ cd repo3
  $ hg rm a
  $ hg st --copies
  R a
  $ hg ci -m 'remove'
  $ hg mv b a
  $ hg st --copies
  A a
    b
  R b
  $ hg commit -m 'blah'
  $ hg st --copies --change .
  A a
    b
  R b
  $ hg st --copies --rev 1
  A a
    b
  R b
  $ hg st --copies --rev 0
  M a
  R b
  $ cd ..

Case 4

The file is explicitly removed in a changesets. Then we moved over with
another file. We finally amend the previous changesets

Status initially reports it as "added" with proper rename information. After
amend report it as Modified without rename information (This match case 1 and 2
behavior after commit)

  $ cd repo4
  $ hg rm a
  $ hg st --copies
  R a
  $ hg ci -m 'remove'
  $ hg mv b a
  $ hg st --copies
  A a
    b
  R b
  $ hg commit --amend --quiet
  $ hg st --copies --change .
  M a
  R b
  $ cd ..

(will be used to test copy detect in changeset)

  $ cp -r repo3 repo4

Revert behavior
===============
(as in 3.1)
All repo are rollbacked to the state prior commit.

Case 1

a: reverted to previous content (with backup)
b: undeleted

(3.2 forget "a". it becomes untracked and previous version not reinstalled)

  $ cd repo1
  $ hg rollback --quiet
  $ hg st --copies
  A a
    b
  R b
  $ hg revert a
  $ hg st --copies
  ? a.orig
  $ cd ..

Case 2

a: reverted to previous content (with backup)
b: untouched (left deleted)

  $ cd repo2

  $ hg rollback --quiet
  $ hg st --copies
  M a
    b
  R b
  $ hg revert a
  $ hg st --copies
  R b
  ? a.orig
  $ cd ..

Case 3

a: reverted to previous content (with backup)
b: undeleted

  $ cd repo3
  $ hg rollback --quiet
  $ hg st --copies --rev 0
  M a
    b
  R b
  $ hg revert a --rev 0
  $ hg st --copies
  A a
    b
  ? a.orig
  $ cd ..

Case 5 (case 4 without the rollback, rollback to base state)

a: reverted to previous content (with backup)
b: untouched

(3.2 marks b as removed))

  $ cd repo4
  $ hg st --copies --rev 0
  M a
  R b
  $ hg revert a --rev 0
  $ hg st --copies
  M a
  $ cd ..
Comment 5 Pierre-Yves David 2014-11-24 15:36 UTC
Current mind state on my side:

1) The bug appears to be in status.

The file should be reported as "Modified" instead of "Added" all others part of mercurial agree (move --force, commit state, amend) to call this a modified file.

2) Our rename detection has trouble, both in status and in revert.
Comment 6 Matt Mackall 2014-11-24 15:40 UTC
That diagnosis appears to disagree with the bisection?
Comment 7 Pierre-Yves David 2014-11-24 15:41 UTC
The bisection point to the place were the baroque status output started to confused revert (leading to bad behavior of revert).
Comment 8 Pierre-Yves David 2014-11-26 18:31 UTC
Current approach to solve this is:

1) stop reporting 'hg rm X; hg add X' as added. it disagree with 'hg mv --force' and status output after commit

2) Look for rename information for working copy modified file too.
Comment 9 Pierre-Yves David 2014-11-26 18:32 UTC
Fix using this approach have been sent to the list.
Comment 10 HG Bot 2014-11-27 13:31 UTC
Fixed by http://selenic.com/repo/hg/rev/2963d5c9d90b
Pierre-Yves David <pierre-yves.david@fb.com>
rename: properly report removed and added file as modified (issue4458)

The result of 'hg rm' + 'hg rename' disagreed with the one from
'hg rename --force'. We align them on 'hg move --force' because it agrees with
what 'hg status' says after the commit.

Stopping reporting a modified file as added puts an end to the hg revert confusion in this
situation (issue4458).

However, reporting the file as modified also prevents revert from restoring the copy
source. We fix this in a later changeset.

Git diff also stop reporting the add in the middle of the chain as add. Not
sure how important (and even wrong) it is.

(please test the fix)
Comment 11 Matt Mackall 2015-01-22 15:04 UTC
Bulk testing -> fixed
Comment 12 HG Bot 2017-12-07 17:55 UTC
Fixed by https://mercurial-scm.org/repo/hg/rev/5b569d512fbd
Yuya Nishihara <yuya@tcha.org>
fancyopts: use getopt.gnu_getopt()

The issue described in the docstring has been fixed since Python 20ab2260dc93,
which is in 2.7.

https://hg.python.org/cpython/rev/20ab2260dc93
https://bugs.python.org/issue4458

This fixes the handling of '--' value.

(please test the fix)
Comment 13 Bugzilla 2017-12-15 00:00 UTC
Bug was set to TESTING for 7 days, resolving