Skip to content

Instantly share code, notes, and snippets.

@nickgr6
Last active July 13, 2022 14:26
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nickgr6/a6837b0c01a4f230bc26 to your computer and use it in GitHub Desktop.
Save nickgr6/a6837b0c01a4f230bc26 to your computer and use it in GitHub Desktop.
Merge pom's via python git driver. This code is highly specific for my own project structure. Use at your own risk.
#! /usr/bin/env python
import sys
import subprocess
import shlex
import codecs
import xml.dom.minidom as dom
class PomProperty:
"""__init__() functions as the class constructor"""
def __init__(self, pomFile, propertKey):
self.propertyKey = propertKey
self.initialValue = get_property_value(propertKey, pomFile)
def change_property(fileContents, propertyKey, old_value, new_value):
propertyOpenTag = "<{0}>".format(propertyKey)
propertyCloseTag = "</{0}>".format(propertyKey)
# Can be uncommented for debug purposes.
# print "Replace: " + propertyOpenTag + old_value + propertyCloseTag
# print "- with:" + propertyOpenTag + new_value + propertyCloseTag
#
# print ""
return fileContents.replace(
propertyOpenTag + old_value + propertyCloseTag,
propertyOpenTag + new_value + propertyCloseTag
)
def get_project_version(f):
try:
tree = dom.parse(f)
version = None
parent_version = None
for entry in tree.documentElement.childNodes:
if entry.nodeName == "version":
version = entry.firstChild.data
if entry.nodeName == "parent":
for entry2 in entry.childNodes:
if entry2.nodeName == 'version':
parent_version = entry2.firstChild.data
if version is not None:
# version has a priority over parent version
return version
else:
# may return None
return parent_version
except:
print(sys.argv[0] + ': error while parsing pom.xml')
return None
def get_property_value(propertyName, file):
try:
tree = dom.parse(file)
version = None
parent_version = None
for entry in tree.documentElement.childNodes:
if entry.nodeName == "properties":
for entry2 in entry.childNodes:
if entry2.nodeName == propertyName:
parent_version = entry2.firstChild.data
if version is not None:
# version has a priority over parent version
return version
else:
# may return None
return parent_version
except:
print('Error while parsing pom.xml')
return None
if (len(sys.argv) < 4 or len(sys.argv) > 5):
print("Wrong number of arguments: " + str(len(sys.argv)))
sys.exit(-1)
baseFile = sys.argv[1]
oursFile = sys.argv[2]
theirsFile = sys.argv[3]
current_branch_version = get_project_version(oursFile) # Ours
other_branch_version = get_project_version(theirsFile) # Theirs
pomPropertiesToKeep = [
PomProperty(oursFile, "module1.version"),
PomProperty(oursFile, "module2.version"),
PomProperty(oursFile, "module3.version"),
PomProperty(oursFile, "module4.version")
]
# Change the 'ours' pom version (including the properties as defined in the 'properties map' above
# to the pom version of the incoming file to avoid merge conflicts.
# Later in the process, the 'ours' pom version is restored.
with codecs.open(oursFile, 'r', 'utf-8') as f:
newTempOursPomContent = f.read()
newTempOursPomContent = change_property(newTempOursPomContent, "version", current_branch_version, other_branch_version)
for pomProperty in pomPropertiesToKeep:
theirsVersion = get_property_value(pomProperty.propertyKey, theirsFile)
if (pomProperty.initialValue != None and theirsVersion != None):
newTempOursPomContent = change_property(
newTempOursPomContent,
pomProperty.propertyKey,
pomProperty.initialValue,
theirsVersion
)
with codecs.open(oursFile, 'w', 'utf-8') as f:
f.write(newTempOursPomContent)
# Start merge the files -> the -p value makes sure, the merge is not performed on the actual files,
# but printed to the terminal. This way we can check if other merge conflicts will occur,
# before restoring our original version values we'd like to keep..
testMergeCommand = "git merge-file -p -L mine -L base -L theirs " + oursFile + " " + baseFile + " " + theirsFile
process = subprocess.Popen(shlex.split(testMergeCommand), stdout=subprocess.PIPE)
git_merge_res = process.communicate()[0]
mergeReturnCode = process.returncode
gitMergeResultPomContents = git_merge_res.strip().decode('utf-8')
# Revert the 'ours' pom version back to it's own original version..
gitMergeResultPomContents = change_property(gitMergeResultPomContents, "version", other_branch_version,
current_branch_version)
# Change all the defined propertie 'versions' back to the original branch value, BUT only
# if the property has the same value as the pom's main <version> value. Or if the 'theirs' value contains '1.feature_',
# because we don't want to take over 'feature specific' dependency versions..
# We do this because:
# - When developing a feature over multiple projects, we can keep this branches 'coupled'
# - But when, for example, the main project doesn't need a specific feature branch, we can update it
# with whatever value the branch is merged with (probably the master branch).
for pomProperty in pomPropertiesToKeep:
theirsVersion = get_property_value(pomProperty.propertyKey, theirsFile)
if (pomProperty.initialValue != None and (
(pomProperty.initialValue == current_branch_version and theirsVersion != None)
or "1.feature_" in theirsVersion
)):
gitMergeResultPomContents = change_property(gitMergeResultPomContents,
pomProperty.propertyKey,
theirsVersion,
pomProperty.initialValue
)
# Write the new contents to 'ours' file as merge result.
with codecs.open(oursFile, 'w', 'utf-8') as f:
f.write(gitMergeResultPomContents)
print "git merge result code: " + str(mergeReturnCode)
## Uncomment following line to always force merge conflict
# sys.exit(5)
sys.exit(mergeReturnCode)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment