Edgewall Software
Modify

Opened 6 years ago

Last modified 22 months ago

#13092 new defect

Unified diff of changeset sometimes include wrong changes?

Reported by: anonymous Owned by:
Priority: normal Milestone: plugin - mercurial
Component: plugin/mercurial Version:
Severity: normal Keywords:
Cc: Branch:
Release Notes:
API Changes:
Internal Changes:

Description (last modified by Jun Omae)

For example the changesets before the merge log:mercurial-plugin@17:

Another example is before the merge log:mercurial-plugin@46:

Attachments (1)

t13092.diff (14.9 KB ) - added by Jun Omae 6 years ago.

Download all attachments as: .zip

Change History (9)

comment:1 by Jun Omae, 6 years ago

Component: version controlplugin/mercurial
Description: modified (diff)
Milestone: plugin - mercurial

comment:2 by Jun Omae, 6 years ago

It seems that MercurialNode.get_previous() with @14 wrongly returns 13. It should return 11. Unified diff for 14/mercurial-plugin should be changes between 11 and 14.

comment:3 by Peter Suter, 6 years ago

Right. Here's a unittest showing the issue:

  • tracext/hg/tests/backend.py

    diff -r c9f2efa16762 tracext/hg/tests/backend.py
    a b  
    473473        self.assertEqual('delete', delete[3])
    474474        self.assertEqual('lunch/lasagna/pasta.txt', delete[0].path)
    475475
     476    def test_get_previous(self):
     477        # regression test for #13092
     478        self._hg_init(data=False)
     479        self._add_repository()
     480        repos = self._repomgr.get_repository('hgrepos')
     481        self._create_commit('lunch', 'lunch/menu.txt')
     482        self._create_commit('pasta', 'lunch/lasagna/pasta.txt')
     483        self._hg_update('0')
     484        self._create_commit('fish', 'lunch/fish/chips.txt')
     485        self._create_commit('coffee', 'lunch/fish/drink.txt', content='coffee')
     486        self._hg_update('1')
     487        self._create_commit('coke', 'lunch/lasagna/drink.txt', content='coke')
     488        repos.repo.invalidate()
     489
     490        node = repos.get_node('/', 4)
     491        prev_rev = node.get_previous()[1]
     492        self.assertEqual(repos.display_rev(1), repos.display_rev(prev_rev))
     493
    476494
    477495def test_suite():
    478496    suite = unittest.TestSuite()

I assume get_previous() using get_history() is ok. So probably get_history() isn't right somehow. I'm guessing %s:0 is a revset x:y but what is needed is a x::y, but for some reason it that x>y doesn't work, so reverse(y::x) must be used instead:

  • tracext/hg/backend.py

    diff -r 21f2fa60ca5f tracext/hg/backend.py
    a b  
    11671167        pats = []
    11681168        if self.str_path:
    11691169            pats.append('path:' + self.str_path)
    1170         opts = {'rev': ['%s:0' % self.changectx.hex()]}
     1170        opts = {'rev': ['reverse(0::%s)' % self.changectx.hex()]}
    11711171        if self.isfile and self.repos.version_info < (2, 1, 1):
    11721172            opts['follow'] = True
    11731173        if arity(cmdutil.walkchangerevs) == 4:

This makes the unittest above pass at least.

by Jun Omae, 6 years ago

Attachment: t13092.diff added

comment:4 by Jun Omae, 6 years ago

attachment:t13092.diff

I consider the get_history() should walk parents of the commit and your changes are right, but log view for mercurial repository would show only ancestors of the given revision after the changes. Currently, the log view shows all branches.

comment:5 by Peter Suter, 6 years ago

Good point! I would not like changing the log to show only ancestors.

(Do Git repositories only show ancestors in the log? Is that intentional?)

in reply to:  5 comment:6 by Jun Omae, 6 years ago

Replying to Peter Suter:

Good point! I would not like changing the log to show only ancestors.

(Do Git repositories only show ancestors in the log? Is that intentional?)

Yes. That behavior on git repository is intentional. On git repository, determining previous revision is cost too much since no integer revisions.

I think it would be good that the log view shows ancestors of the given revision or branch by default and add an option to show all branches without the given revision.

comment:7 by Peter Suter, 6 years ago

Have you considered if the bug is in using repos.get_node(new_path, new).get_previous() for the old_rev of a changeset, instead of e.g. repos.previous_rev(new, new_path)? That seems to give the "correct" changeset.

(For Git: Maybe Git 2.18+ commit-graph feature would be useful? It sounds somewhat similar.)

in reply to:  7 comment:8 by Jun Omae, 6 years ago

Replying to Peter Suter:

Have you considered if the bug is in using repos.get_node(new_path, new).get_previous() for the old_rev of a changeset, instead of e.g. repos.previous_rev(new, new_path)? That seems to give the "correct" changeset.

The Node.get_previous() for Subversion is absolutely clear and correct. However, it is ambiguous for non-linear repository (e.g. Git, Mercurial). I consider it should always return an ancestor of the node.

(For Git: Maybe Git 2.18+ commit-graph feature would be useful? It sounds somewhat similar.)

Not useful. We cannot read commit-graph cache because it is binary. We still have to construct the graph and sorting of revisions using git rev-list.

$ file .git/objects/info/commit-graph
.git/objects/info/commit-graph: data
$ git commit-graph read
header: 43475048 1 1 4 0
num_commits: 19368
chunks: oid_fanout oid_lookup commit_metadata large_edges

Modify Ticket

Change Properties
Set your email in Preferences
Action
as new The ticket will remain with no owner.
The ticket will be disowned.
as The resolution will be set. Next status will be 'closed'.
The owner will be changed from (none) to anonymous. Next status will be 'assigned'.

Add Comment


E-mail address and name can be saved in the Preferences .
 
Note: See TracTickets for help on using tickets.