Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

EMF Compare/Development/Conflicts Involving Refined Differences

Conflicts that directly or indirectly involve redefined differences may come in the following four different faces. Note that they can occur in both sides, left- and right-hand side of a three-way merge scenario, whereas we only show one side to represent both.

  1. Refining difference vs. refining difference
  2. Refined difference vs. refining difference
  3. Refined difference vs. refined difference
  4. Conflict of mixed refined and refining differences on one or two sides for some or all refining diffs of the respective refined diffs.


EMFCompare Development Refined Differences Conflicts Involving Refined Differences.png

Legend: lightning symbol → conflict, purple rectangle → refining diff (atomic), blue rectangle → refined diff (composite diff), Line between diff and conflict: Conflict among linked differences

Problem Description

It is unclear, what the correct structure for conflicts involving refined and refining diffs should be and what the consequences for either structure are regarding the user interface.

In general, refined/refining differences have a few important consequences in the user interface, but also in the merging behavior.

  1. Refining differences are hidden by default in the structure merge viewer, if they are subclasses UMLDiffs.
  2. If visible, refining differences are shown as children of the refined diff instead of as children of their original match.
  3. If a refined difference is merged, all its refining are merged too.
  4. If a refining difference is merged, the refined difference -- and consequently all other refining diffs -- are merged too.

Current Behavior (before EMF Compare 3.3)

When conflicts are involved, the ConflictsGroupImpl currently behaves the following way for the above mentioned four structures of conflicts involving refined/refining diffs:

  1. Show refining diffs (the ones that are referenced by the conflict). If they are UMLDiff elements, they will be hidden by the UML Refined Elements filters. If all diffs are hidden, also the entire conflict is hidden. However, the refined element doesn’t show up in the conflict, because it is not directly referenced from the conflict.
  2. Show refined diff on the left-hand side, atomic diff on the right-hand side. If the right-hand side diff is a UMLDiff, it’ll be hidden, so that only one diff is shown in the conflict (--> Problem).
  3. Show both refined diffs on the left- and right-hand side. If visible, all refining diffs are shown as children of the refined diffs.
  4. Same as (3), if all refined diffs of the respective refining diffs are contained in the conflict. Otherwise, they’ll show alongside the refined diff (cf. org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.impl.ThreeWayComparisonGroupProvider.ConflictsGroupImpl.mustDisplayAsDirectChildOfMatch(Diff)).

Open Questions and Discussion

What is the meaning of a refined diff exactly?

Should it be seen as an aggregation effectively replacing its refining diffs? Or is it just additional information on the refining diffs?

My impression is that we use it entirely as an aggregation intended to replace its refining diffs for users. The refining diffs are in this use case certainly still very important on a technical level, but in terms of user interfaces, we don’t want to show them and let users only interact with the refined diff instead. To users, refined diffs are an own unit encompassing and conceptually replacing the atomic diffs.

This meaning of refined diffs is also manifested in a couple of places in the code. For instance, when accepting or rejecting a refining diff, we currently also merge automatically the refined diff. This is caused by org.eclipse.emf.compare.internal.utils.ComparisonUtil.getAssociatedDiffs(Diff, Iterable<Diff>, LinkedHashSet<Diff>, boolean, boolean) line 448; i.e., the inclusion of the diff.getRefines() in the "associated" diffs and consequently in the sub diffs and as a further consequent in the direct resulting merges. Thus, refining diffs are not mergeable individually anyway. This "rule" is quite implicit in the code, but it is specified this way. In the UMLRTPostProcessor, we also turn scenarios (1) into scenarios (2) to make sure that the refined diff shows up in the conflict in the structure merge viewer, also if only their refining diffs are conflicting. Note that we only do this for UI purposes, which is not very nice, as we “throw away” the technically correct information on which refining diffs are actually conflicting.

Recently, there was also a change proposed in change 75720, where all refining diffs are added to the conflict of the refined diff; essentially to turn scenario (3) into a scenario (4) so that all refining diffs always are part of the conflicts.

I’d suggest to decide explicitly for one or the other meaning of refined differences and then implement this more consequently and explicitly. I’d rather vote for refined diffs that act as aggregating units that conceptually replace their refining diffs.

Are there distinct use cases for the different conflict structures (1) - (4)?

With the meaning of refined differences as a unit aggregating and replacing refining differences in mind, all four cases mean the same thing to the user: there is a conflict between two or more refined differences, independently of how the conflict is structured internally (i.e. structure (1) - (4)). As they have the same meaning for users, I’d argue that, consequently, they all should end up being shown to the user in the same way.

Technically, there still may be different meanings between (1) - (4) that we might be interested in when handling conflicts. For instance, the structure (1) is a conflict between two refined differences, whereas the conflict has been caused probably by the generic conflict engine among their contained refining differences. This should actually be the most common use case: the refined diffs are aggregations, but the same conflict rules still apply as for their refining differences in most cases. I’d say that the structures (2) and (3) indicate custom conflict rules for refined diffs, whereas (2) is more explicit what refining diff caused the conflict with the other refined diff and (3) is a custom conflict rule among refined differences only.

However, given the meaning of refined differences as strongly coupled units, I don’t see a lot of value in the structure (4), neither if only a subset or all the refining differences are part of the conflict alongside their refined differences. Structure (4) only has a purpose if we don’t accept the general meaning of refined differences as mentioned above (aggregation). Then we would use (4) to force all components of EMF Compare to use or not use this meaning of handling them as a strongly coupled unit. However, several parts of EMF Compare don’t explicitly distinguish among them adequately and some already implement this meaning anyway.

Why are only refining diffs of UMLDiffs hidden by default?

Shouldn’t this be the default for all refining diffs?

If we accept the abovementioned meaning of refined differences, why are only refining diffs of UMLDiffs hidden and not all refining diffs? I’d argue that, given abovementioned meaning, they should be filtered in general and not only if they are refining UMLDiffs; this would manifest the meaning more explicitly.

UI change vs Diff model change

In the spirit of manifesting the meaning of refined/refining differences more explicitly, I suggest to let the user interface look exactly the same for all four structures (1) - (4) shown above because to the user, they all should just mean that two (refined) differences are conflicting.

The next question would be to homogenize those four structures during or after conflict detection phase in the diff model or make the ConflictsGroupImpl smart enough to handle all four cases. I’m a bit struggling to have a clear opinion on that. First I had the opinion that the UI should add as little logic as possible and mainly just show the diff model as is. However, this would mean that we’d have to homogenize the four structures during/after the conflict detection to be able to show the conflicts to the user the same way in all cases. Unfortunately, the homogenization would remove quite interesting information that was available in the diff model (cf. discussion on different meanings between (1) - (3)). So eventually, I’m now slightly leaning towards letting the ConflictsGroupImpl handle the four structures in order to just present them homogeneously to the user. Nevertheless, users should have the option to view the plain conflicts (e.g. by an alternative group provider or a preference), as this may be a necessary for analysis purposes.

Note that showing always the refined diff (e.g., in structure (1) and (2)), may mean that we have to join multiple conflicts into one aggregated conflict too, because two refined diffs may indirectly be in conflict multiple times through their refining differences. In EMF Compare, we have the rule that one difference must only be in one conflict at a time (single-valued reference in Diff). Thus, normal differences can only show up once across all conflicts. We should not break this rule with refined differences, by showing them in multiple conflicts. Thus, we may have to join all conflicts that essentially put the same two refined diffs in one conflict.

Final thought: Shouldn’t refined differences be named composite diffs?

The terms refines/refinedBy or refining and refined diff is pretty confusing. If we ever get the chance to do a rename, I’d propose composite difference instead of refined difference and consistsOf instead of refinedBy.

Back to the top