Skip to content

Commit

Permalink
[GR-54684] Support recursive merges in mx mergetool-suite-import
Browse files Browse the repository at this point in the history
PullRequest: mx/1807
  • Loading branch information
zapster committed Jun 13, 2024
2 parents aeb868f + 333edde commit 2fae8cf
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 42 deletions.
16 changes: 8 additions & 8 deletions common.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

"COMMENT.jdks": "When adding or removing JDKs keep in sync with JDKs in ci/common.jsonnet",
"jdks": {
"galahad-jdk": {"name": "jpg-jdk", "version": "23", "build_id": "jdk-23+26-2139", "platformspecific": true, "extrabundles": ["static-libs"]},
"galahad-jdk": {"name": "jpg-jdk", "version": "24", "build_id": "jdk-24+1-1", "platformspecific": true, "extrabundles": ["static-libs"]},

"oraclejdk11": {"name": "jpg-jdk", "version": "11.0.11", "build_id": "jdk-11.0.11+9", "platformspecific": true, "extrabundles": ["static-libs"] },

Expand Down Expand Up @@ -45,13 +45,13 @@
"labsjdk-ee-21-llvm": {"name": "labsjdk", "version": "ee-21.0.2+13-jvmci-23.1-b33-sulong", "platformspecific": true },
"graalvm-ee-21": {"name": "graalvm-java21", "version": "23.1.3", "platformspecific": true },

"oraclejdk-latest": {"name": "jpg-jdk", "version": "23", "build_id": "jdk-23+26", "platformspecific": true, "extrabundles": ["static-libs"]},
"labsjdk-ce-latest": {"name": "labsjdk", "version": "ce-23+26-jvmci-b01", "platformspecific": true },
"labsjdk-ce-latestDebug": {"name": "labsjdk", "version": "ce-23+26-jvmci-b01-debug", "platformspecific": true },
"labsjdk-ce-latest-llvm": {"name": "labsjdk", "version": "ce-23+26-jvmci-b01-sulong", "platformspecific": true },
"labsjdk-ee-latest": {"name": "labsjdk", "version": "ee-23+26-jvmci-b01", "platformspecific": true },
"labsjdk-ee-latestDebug": {"name": "labsjdk", "version": "ee-23+26-jvmci-b01-debug", "platformspecific": true },
"labsjdk-ee-latest-llvm": {"name": "labsjdk", "version": "ee-23+26-jvmci-b01-sulong", "platformspecific": true }
"oraclejdk-latest": {"name": "jpg-jdk", "version": "24", "build_id": "jdk-24+1", "platformspecific": true, "extrabundles": ["static-libs"]},
"labsjdk-ce-latest": {"name": "labsjdk", "version": "ce-24+1-jvmci-b01", "platformspecific": true },
"labsjdk-ce-latestDebug": {"name": "labsjdk", "version": "ce-24+1-jvmci-b01-debug", "platformspecific": true },
"labsjdk-ce-latest-llvm": {"name": "labsjdk", "version": "ce-24+1-jvmci-b01-sulong", "platformspecific": true },
"labsjdk-ee-latest": {"name": "labsjdk", "version": "ee-24+1-jvmci-b01", "platformspecific": true },
"labsjdk-ee-latestDebug": {"name": "labsjdk", "version": "ee-24+1-jvmci-b01-debug", "platformspecific": true },
"labsjdk-ee-latest-llvm": {"name": "labsjdk", "version": "ee-24+1-jvmci-b01-sulong", "platformspecific": true }
},

"eclipse": {
Expand Down
153 changes: 120 additions & 33 deletions src/mx/_impl/mergetool.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,95 @@
from . import mx


def remove_conflict_markers(filename):
"""
Removes conflicts markers from a file's content, keeps only the content of the first
conflict branch and returns the result.
Merge conflict marker might be nested. A basic example looks like:
"imports": {
"suites": [
{
"name": "substratevm",
"subdir": True,
<<<<<<< HEAD
"version": "db85a24b2358c7ebf7850a3cfd7a0e28e693cbb1",
=======
"version": "2449afbb28dadf2d9a70a866da06d664f1d1a539",
>>>>>>> upstream/master
"urls": [
{"url": "https://github.com/oracle/graal.git", "kind": "git"},
]
},
],
},
A more complex example would be:
"imports": {
"suites": [
{
"name": "substratevm",
"subdir": True,
<<<<<<< HEAD
"version": "db85a24b2358c7ebf7850a3cfd7a0e28e693cbb1",
||||||| merged common ancestors
<<<<<<<<< Temporary merge branch 1
"version": "f1dffe84fdce6f45a42528e4f114cd8c52d1aae1",
||||||||| merged common ancestors
<<<<<<<<<<< Temporary merge branch 1
"version": "414d874be3af1a21a3c8ebfd9cbd163b985a0a68",
||||||||||| 68ef48ab8db
"version": "69baa168b4c0602b078f0bc0b49d95b93582ba95",
===========
"version": "5b5a1351f3a02177e0878d5f2cd01c56507e8ba4",
>>>>>>>>>>> Temporary merge branch 2
=========
"version": "06ad740e1c0817d3d555b514ced92dd4465d32db",
>>>>>>>>> Temporary merge branch 2
=======
"version": "2449afbb28dadf2d9a70a866da06d664f1d1a539",
>>>>>>> upstream/master
"urls": [
{"url": "https://github.com/oracle/graal.git", "kind": "git"},
]
},
],
},
"""
with open(filename, "r") as fp:
suite_content = fp.read()
result = []
conflict_level = 0
keep = True
for line in suite_content.splitlines(keepends=True):
if line.startswith("<<"):
if conflict_level > 0:
# inner conflict scope -> stop printing
keep = False
conflict_level += 1
continue
if line.startswith("||"):
# in a conflict scope and not in the first branch -> stop printing
keep = False
continue
if line.startswith("=="):
# in a conflict scope and not in the first branch -> stop printing
keep = False
continue
if line.startswith(">>"):
conflict_level -= 1
if conflict_level == 0:
# end of conflict scope -> restart printing
keep = True
continue
if keep:
result.append(line)
return "".join(result)


@mx.suite_context_free
def mergetool_suite_import(args):
parser = argparse.ArgumentParser(
Expand Down Expand Up @@ -107,34 +196,27 @@ def _assert_or_fallback(cond, message=None):

_assert_or_fallback("suite.py" in os.path.basename(merged), "Only merge suite.py files. Falling back to diff3")

def read_suite_imports(filename):
with open(filename) as suite_fp:
my_globals = {}
my_locals = {}
suite_content = suite_fp.read()
try:
exec(suite_content, my_globals, my_locals) # pylint: disable=exec-used
except Exception as ex: # pylint: disable=broad-except
msg = f"Cannot load suite file {filename}: {ex}"
if any((x.startswith("<<<<<<<<<") for x in suite_content.splitlines())):
msg += textwrap.dedent(
"""
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
Hint: conflict marker detected.
Try using a non-recursive merge strategy, e.g.:
git merge -s resolve
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
"""
)
_fallback(msg)
return my_locals.get("suite", {}).get("imports", {}).get("suites")
def read_suite_imports(suite_content, filename):
my_globals = {}
my_locals = {}
try:
exec(suite_content, my_globals, my_locals) # pylint: disable=exec-used
except Exception as ex: # pylint: disable=broad-except
msg = f"Cannot load suite file {filename}: {ex}"
_fallback(msg)
return my_locals.get("suite", {}).get("imports", {}).get("suites")

def to_import_dict(suite_imports):
return {s["name"]: s["version"] for s in suite_imports if "version" in s}

local_imports = read_suite_imports(local)
remote_imports = read_suite_imports(remote)
base_imports = read_suite_imports(base)
local_content = remove_conflict_markers(local)
remote_content = remove_conflict_markers(remote)
base_content = remove_conflict_markers(base)

local_imports = read_suite_imports(local_content, local)
remote_imports = read_suite_imports(remote_content, remote)
base_imports = read_suite_imports(base_content, base)

local_import_dict = to_import_dict(local_imports)
remote_import_dict = to_import_dict(remote_imports)
base_import_dict = to_import_dict(base_imports)
Expand All @@ -152,24 +234,25 @@ def to_import_dict(suite_imports):
_assert_or_fallback(mismatches, "Not import mismatches. Falling back to diff3")

# fix mismatches
with open(remote, mode="r+b") as fp:
remote_content = fp.read()
with open(base, mode="r+b") as fp:
base_content = fp.read()

for s in mismatches:
local_rev = local_import_dict[s].encode()
remote_rev = remote_import_dict[s].encode()
base_rev = base_import_dict[s].encode()
local_rev = local_import_dict[s]
remote_rev = remote_import_dict[s]
base_rev = base_import_dict[s]
remote_content = remote_content.replace(remote_rev, local_rev)
base_content = base_content.replace(base_rev, local_rev)

new_local = None
new_remote = None
new_base = None
try:
# fmt: off
with tempfile.NamedTemporaryFile(mode="w+b", delete=False) as new_remote_fp, \
tempfile.NamedTemporaryFile(mode="w+b", delete=False) as new_base_fp:
with tempfile.NamedTemporaryFile(mode="w", delete=False) as new_local_fp, \
tempfile.NamedTemporaryFile(mode="w", delete=False) as new_remote_fp, \
tempfile.NamedTemporaryFile(mode="w", delete=False) as new_base_fp:
# fmt: on
new_local_fp.write(local_content)
new_local = new_local_fp.name
new_remote_fp.write(remote_content)
new_remote = new_remote_fp.name
new_base_fp.write(base_content)
Expand All @@ -179,3 +262,7 @@ def to_import_dict(suite_imports):
finally:
if new_local:
os.unlink(new_local)
if new_remote:
os.unlink(new_remote)
if new_base:
os.unlink(new_base)
2 changes: 1 addition & 1 deletion src/mx/_impl/mx.py
Original file line number Diff line number Diff line change
Expand Up @@ -18173,7 +18173,7 @@ def alarm_handler(signum, frame):
_CACHE_DIR = get_env('MX_CACHE_DIR', join(dot_mx_dir(), 'cache'))

# The version must be updated for every PR (checked in CI) and the comment should reflect the PR's issue
version = VersionSpec("7.25.13") # GR-54672
version = VersionSpec("7.25.14") # mergetool-suite-import conflict marker

_mx_start_datetime = datetime.utcnow()

Expand Down

0 comments on commit 2fae8cf

Please sign in to comment.