Automatic Merge Conflict Resolving for git

This post describes how merge conflicts can be automatically resolved using maven POM files as an example. The example related to the dependencies section of Maven POM files depicted below:

merge conflicts can be automatically resolved using maven POM files

The highlighting indicates that dependency order is different. This reflects what IDEs do automatically and also interactive user manipulation. When feature branches are created dependencies may need to be updated or increased; they aren’t typically reversed or reverted to a lower or earlier version.

Lets look at what happens if we use git’s built-in diff3 based merge:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>groupId</groupId>
  <artifactId>git-merge-driver</artifactId>
  <version>7.0.4</version>

  <dependencies>
<<<<<<< HEAD
    <dependency>
      <groupId>com.deltaxml.merge</groupId>
      <artifactId>merge</artifactId>
      <version>7.1.0</version>
    </dependency>
=======
>>>>>>> v2
    <dependency>
      <groupId>com.deltaxml.merge</groupId>
      <artifactId>flexlm</artifactId>
      <version>7.1.0</version>
    </dependency>
    <dependency>
      <groupId>com.deltaxml.merge</groupId>
<<<<<<< HEAD
      <artifactId>deltaxml</artifactId>
      <version>10.3.0</version>
=======
      <artifactId>flexlm</artifactId>
      <version>7.0.6</version>
    </dependency>
    <dependency>
      <groupId>com.deltaxml.merge</groupId>
      <artifactId>merge</artifactId>
      <version>7.0.6</version>
>>>>>>> v2
    </dependency>
  </dependencies>
</project>

Here’s my first attempt to use our XML Merge with rule processing:

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:deltaxml="https://www.deltaxml.com/ns/well-formed-delta-v1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" deltaxml:version-order="ancestor, edit1, edit2" deltaxml:content-type="merge-concurrent" deltaxml:version="2.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" deltaxml:deltaV2="ancestor=edit1!=edit2">
   <modelVersion deltaxml:deltaV2="ancestor=edit1=edit2">4.0.0</modelVersion>
   <groupId deltaxml:deltaV2="ancestor=edit1=edit2">groupId</groupId>
   <artifactId deltaxml:deltaV2="ancestor=edit1=edit2">git-merge-driver</artifactId>
   <version deltaxml:deltaV2="ancestor=edit1=edit2">7.0.4</version>
   <dependencies deltaxml:deltaV2="ancestor=edit1!=edit2">
      <dependency deltaxml:edit-type="delete" deltaxml:deltaV2="ancestor=edit1">
         <groupId>com.deltaxml.merge</groupId>
         <artifactId>merge</artifactId>
         <version>7.1.0</version>
      </dependency>
      <dependency deltaxml:deltaV2="ancestor=edit1=edit2">
         <groupId>com.deltaxml.merge</groupId>
         <artifactId>flexlm</artifactId>
         <version>7.1.0</version>
      </dependency>
      <dependency deltaxml:deltaV2="ancestor=edit1=edit2">
         <groupId>com.deltaxml.merge</groupId>
         <artifactId>deltaxml</artifactId>
         <version>10.3.0</version>
         <version>7.0.6</version>
      </dependency>
      <dependency deltaxml:deltaV2="ancestor=edit1=edit2">
         <groupId>com.deltaxml.merge</groupId>
         <artifactId>merge</artifactId>
         <version>7.0.6</version>
      </dependency>
   </dependencies>
</project>
dev/git-merge

It’s easier to see the dependencies, but why are there two version children in one of them? I delved a little deeper… The above result was generated using our command line driver for concurrent3 using the default parameter settings. An important one here was element splitting. What was happening was there was a textGroup containing the version numbers (‘10.3.0’ and ‘7.0.6’) and that was the only content of the element. As there was a high proportion of change it was then split. The splitting resulted into logically two additions which were then rule processed and added.

Lets look at this without splitting, word-by-word or rule processing applied:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:deltaxml="https://www.deltaxml.com/ns/well-formed-delta-v1"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         deltaxml:version-order="a, v1, v2"
         deltaxml:content-type="merge-concurrent"
         deltaxml:version="2.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
         deltaxml:deltaV2="a!=v1!=v2">
   <modelVersion deltaxml:deltaV2="a=v1=v2">4.0.0</modelVersion>
   <groupId deltaxml:deltaV2="a=v1=v2">groupId</groupId>
   <artifactId deltaxml:deltaV2="a=v1=v2">git-merge-driver</artifactId>
   <version deltaxml:deltaV2="a=v1=v2">7.0.4</version>
   <dependencies deltaxml:deltaV2="a!=v1!=v2">
      <dependency deltaxml:deltaV2="a!=v1">
         <groupId deltaxml:deltaV2="a=v1">com.deltaxml.merge</groupId>
         <artifactId deltaxml:deltaV2="a=v1">merge</artifactId>
         <version deltaxml:deltaV2="a!=v1">
            <deltaxml:textGroup deltaxml:deltaV2="a!=v1">
               <deltaxml:text deltaxml:deltaV2="a">7.0.4</deltaxml:text>
               <deltaxml:text deltaxml:deltaV2="v1">7.1.0</deltaxml:text>
            </deltaxml:textGroup>
         </version>
      </dependency>
      <dependency deltaxml:deltaV2="a=v2!=v1">
         <groupId deltaxml:deltaV2="a=v1=v2">com.deltaxml.merge</groupId>
         <artifactId deltaxml:deltaV2="a=v2!=v1">
            <deltaxml:textGroup deltaxml:deltaV2="a=v2!=v1">
               <deltaxml:text deltaxml:deltaV2="a=v2">deltaxml</deltaxml:text>
               <deltaxml:text deltaxml:deltaV2="v1">flexlm</deltaxml:text>
            </deltaxml:textGroup>
         </artifactId>
         <version deltaxml:deltaV2="a=v2!=v1">
            <deltaxml:textGroup deltaxml:deltaV2="a=v2!=v1">
               <deltaxml:text deltaxml:deltaV2="a=v2">10.0.0</deltaxml:text>
               <deltaxml:text deltaxml:deltaV2="v1">7.1.0</deltaxml:text>
            </deltaxml:textGroup>
         </version>
      </dependency>
      <dependency deltaxml:deltaV2="a!=v1!=v2">
         <groupId deltaxml:deltaV2="a=v1=v2">com.deltaxml.merge</groupId>
         <artifactId deltaxml:deltaV2="a=v2!=v1">
            <deltaxml:textGroup deltaxml:deltaV2="a=v2!=v1">
               <deltaxml:text deltaxml:deltaV2="a=v2">flexlm</deltaxml:text>
               <deltaxml:text deltaxml:deltaV2="v1">deltaxml</deltaxml:text>
            </deltaxml:textGroup>
         </artifactId>
         <version deltaxml:deltaV2="a!=v1!=v2">
            <deltaxml:textGroup deltaxml:deltaV2="a!=v1!=v2">
               <deltaxml:text deltaxml:deltaV2="a">7.0.4</deltaxml:text>
               <deltaxml:text deltaxml:deltaV2="v1">10.3.0</deltaxml:text>
               <deltaxml:text deltaxml:deltaV2="v2">7.0.6</deltaxml:text>
            </deltaxml:textGroup>
         </version>
      </dependency>
      <dependency deltaxml:deltaV2="v2">
         <groupId>com.deltaxml.merge</groupId>
         <artifactId>merge</artifactId>
         <version>7.0.6</version>
      </dependency>
   </dependencies>
</project>

Now we can see the artifacts are not aligning and this is because dependencies are an unordered set. We can fix this with orderless and in the current XML merge by keying, so for example:

<dependencies deltaxml:ordered="false">
  <dependency deltaxml:key="com.deltaxml.merge:merge">
    <groupId>com.deltaxml.merge</groupId>
    <artifactId>merge</artifactId>
    <version>7.0.4</version>
  </dependency>
  ...

With this we get our expected alignment. Here we’ve done rule processing and the second dependency had some non-conflicting changes, but we don’t see these after rule processing. However the three way version conflicts remain:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:deltaxml="https://www.deltaxml.com/ns/well-formed-delta-v1"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         deltaxml:version-order="ancestor, mine, theirs"
         deltaxml:content-type="merge-concurrent"
         deltaxml:version="2.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
         deltaxml:deltaV2="ancestor!=mine!=theirs">
   <modelVersion deltaxml:deltaV2="ancestor=mine=theirs">4.0.0</modelVersion>
   <groupId deltaxml:deltaV2="ancestor=mine=theirs">groupId</groupId>
   <artifactId deltaxml:deltaV2="ancestor=mine=theirs">git-merge-driver</artifactId>
   <version deltaxml:deltaV2="ancestor=mine=theirs">7.0.4</version>
   <dependencies deltaxml:deltaV2="ancestor!=mine!=theirs">
      <dependency deltaxml:key="com.deltaxml.merge:merge" deltaxml:deltaV2="ancestor!=mine!=theirs">
         <groupId deltaxml:deltaV2="ancestor=mine=theirs">com.deltaxml.merge</groupId>
         <artifactId deltaxml:deltaV2="ancestor=mine=theirs">merge</artifactId>
         <version deltaxml:deltaV2="ancestor!=mine!=theirs">
            <deltaxml:textGroup deltaxml:deltaV2="ancestor!=mine!=theirs" deltaxml:edit-type="modify">
               <deltaxml:text deltaxml:deltaV2="ancestor">7.0.4</deltaxml:text>
               <deltaxml:text deltaxml:deltaV2="mine">7.1.0</deltaxml:text>
               <deltaxml:text deltaxml:deltaV2="theirs">7.0.6</deltaxml:text>
            </deltaxml:textGroup>
         </version>
      </dependency>
      <dependency deltaxml:key="com.deltaxml.merge:deltaxml" deltaxml:deltaV2="ancestor=mine=theirs">
         <groupId>com.deltaxml.merge</groupId>
         <artifactId>deltaxml</artifactId>
         <version>10.3.0</version>
      </dependency>
      <dependency deltaxml:key="com.deltaxml.merge:flexlm" deltaxml:deltaV2="ancestor!=mine!=theirs">
         <groupId deltaxml:deltaV2="ancestor=mine=theirs">com.deltaxml.merge</groupId>
         <artifactId deltaxml:deltaV2="ancestor=mine=theirs">flexlm</artifactId>
         <version deltaxml:deltaV2="ancestor!=mine!=theirs">
            <deltaxml:textGroup deltaxml:deltaV2="ancestor!=mine!=theirs" deltaxml:edit-type="modify">
               <deltaxml:text deltaxml:deltaV2="ancestor">7.0.4</deltaxml:text>
               <deltaxml:text deltaxml:deltaV2="mine">7.1.0</deltaxml:text>
               <deltaxml:text deltaxml:deltaV2="theirs">7.0.6</deltaxml:text>
            </deltaxml:textGroup>
         </version>
      </dependency>
   </dependencies>
</project>

Now do we want my local branch or the remote or theirs in this case? I’d argue we want the highest version, irrespective of which branch its on; given that APIs are generally upwardly compatible it should hopefully work for all branches.

Luckily we can do this automatically in XSLT: here’s a POM dependency resolver:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:math="http://www.w3.org/2005/xpath-functions/math"
  xmlns:deltaxml="https://www.deltaxml.com/ns/well-formed-delta-v1"
  xmlns:pom="http://maven.apache.org/POM/4.0.0"
  exclude-result-prefixes="xs math"
  version="3.0">
  <xsl:mode on-no-match="shallow-copy"/>


  <!-- pom versions conflicts will have a version element with a
      nested textGroup.  If WordByWord=false and Splitting=false
      then there should be no other non-whitespace text or
      elements inside the version -->
  <xsl:template match="pom:dependency/pom:version[deltaxml:textGroup]
                [empty(* except deltaxml:textGroup) and empty(text()[normalize-space(.) ne ''])]">
    <xsl:copy>
      <!-- https://stackoverflow.com/questions/40202510/xsl-sort-numbers-separated-by-periods -->
      <xsl:sequence select="sort(deltaxml:textGroup/deltaxml:text/text(), (), function($t){tokenize($t, '\.')!xs:integer(.)})[last()]"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

This will ‘auto-resolve’ the version conflicts (which are going to be three way by definition). One final thing to consider – should this filter fix up deltas as it goes, so for example the deltaV2 on the parent dependency element, or should it just do a simple local change and we then have a final process which checks for any unresolved conflicts and when none are found removes all deltaxml attributes including deltas, version-order and content-type?

Putting this all together

The existing git merge driver: deltaxml/git-merge-driver will need an input filter for orderless and keying and also the above output filter and possibly as discussed above a filter to tidy up delta attributes.

This necessitates changing the driver code and the API changes needed require the move from the ThreeWayMerge class to the ConcurrentMerge class.

Future

There are certainly possibilities to extend this further; dependencies in POMs was the first example that comes to mind, it wasn’t an exhaustive analysis of the POM syntax.

This post has concentrated on an example and use-case for automatic conflict resolution, a future post will look at interactive conflict resolution.

Keep Reading

Cyber Resilience for SMEs: A Chat with DeltaXML’s Systems Administrator

Peter Anderson, IT System Administrator, relays the importance of cyber resilience for SMEs.

S1000D and Beyond: Navigating the Skies of Aviation Data with XML

/
This blog explores the significance of XML in aviation data management, highlighting standards like S1000D.

Mastering Table Comparison & Merging: What’s New in XML Merge 11.0.0

XML Merge (11.0.0) and DITA Merge (7.0.0) introduce an enhanced table comparison.

Making Tax Digital: Embracing XML Technology for HMRC Compliance

The Making Tax Digital (MTD) initiative by HMRC aims to digitise the UK tax system, but what does that mean for UK businesses?

Best Practices for Managing XML Configurations in System Administration

Effective management of XML configurations is crucial for system administrators.

The Crucial Role of XML and JSON in Modern Air Traffic Control Operations

XML and JSON play a crucial role in modern air traffic control, facilitating efficient systems.

Customer Voices, Product Success: Utilising Feedback for an Impactful Roadmap

DeltaXML harnesses customer feedback to enhance their XML and JSON comparison tools.

Never miss an update

Sign up to our newsletter and never miss an update on upcoming features or new products