aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Turner <mattst88@gentoo.org>2024-05-28 13:23:30 -0400
committerMatt Turner <mattst88@gentoo.org>2024-06-07 11:02:43 -0400
commit772aa90ea8bff350cd31887f37cc8d5cacfa8dad (patch)
treee149446ac3194d3e1d0754e0c846fcb6404444ed
parentbin/merge-driver-ekeyword: Don't pass program name to main() (diff)
downloadgentoolkit-772aa90ea8bff350cd31887f37cc8d5cacfa8dad.tar.gz
gentoolkit-772aa90ea8bff350cd31887f37cc8d5cacfa8dad.tar.bz2
gentoolkit-772aa90ea8bff350cd31887f37cc8d5cacfa8dad.zip
bin/merge-driver-ekeyword: Move logic to a separate module
To facilitate unit testing. Signed-off-by: Matt Turner <mattst88@gentoo.org>
-rwxr-xr-xbin/merge-driver-ekeyword136
-rw-r--r--pym/gentoolkit/merge_driver_ekeyword/__init__.py0
-rwxr-xr-xpym/gentoolkit/merge_driver_ekeyword/merge_driver_ekeyword.py143
3 files changed, 145 insertions, 134 deletions
diff --git a/bin/merge-driver-ekeyword b/bin/merge-driver-ekeyword
index 59527bb..53dbcad 100755
--- a/bin/merge-driver-ekeyword
+++ b/bin/merge-driver-ekeyword
@@ -3,141 +3,9 @@
# Copyright 2020-2024 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2 or later
-"""
-Custom git merge driver for handling conflicts in KEYWORDS assignments
-
-See https://git-scm.com/docs/gitattributes#_defining_a_custom_merge_driver
-"""
-
-import difflib
-import os
-import shutil
import sys
-import tempfile
-
-from typing import Optional
-from collections.abc import Sequence
-
-from gentoolkit.ekeyword import ekeyword
-
-
-KeywordChanges = list[tuple[Optional[list[str]], Optional[list[str]]]]
-
-
-def keyword_array(keyword_line: str) -> list[str]:
- # Find indices of string inside the double-quotes
- i1: int = keyword_line.find('"') + 1
- i2: int = keyword_line.rfind('"')
-
- # Split into array of KEYWORDS
- return keyword_line[i1:i2].split(' ')
-
-
-def keyword_line_changes(old: str, new: str) -> KeywordChanges:
- a: list[str] = keyword_array(old)
- b: list[str] = keyword_array(new)
-
- s = difflib.SequenceMatcher(a=a, b=b)
-
- changes: KeywordChanges = []
- for tag, i1, i2, j1, j2 in s.get_opcodes():
- if tag == 'replace':
- changes.append((a[i1:i2], b[j1:j2]),)
- elif tag == 'delete':
- changes.append((a[i1:i2], None),)
- elif tag == 'insert':
- changes.append((None, b[j1:j2]),)
- else:
- assert tag == 'equal'
- return changes
-
-
-def keyword_changes(ebuild1: str, ebuild2: str) -> Optional[KeywordChanges]:
- with open(ebuild1) as e1, open(ebuild2) as e2:
- lines1 = e1.readlines()
- lines2 = e2.readlines()
-
- diff = difflib.unified_diff(lines1, lines2, n=0)
- assert next(diff) == '--- \n'
- assert next(diff) == '+++ \n'
-
- hunk: int = 0
- old: str = ''
- new: str = ''
-
- for line in diff:
- if line.startswith('@@ '):
- if hunk > 0:
- break
- hunk += 1
- elif line.startswith('-'):
- if old or new:
- break
- old = line
- elif line.startswith('+'):
- if not old or new:
- break
- new = line
- else:
- if 'KEYWORDS=' in old and 'KEYWORDS=' in new:
- return keyword_line_changes(old, new)
- return None
-
-
-def apply_keyword_changes(ebuild: str, pathname: str,
- changes: KeywordChanges) -> int:
- result: int = 0
-
- with tempfile.TemporaryDirectory() as tmpdir:
- # ekeyword will only modify files named *.ebuild, so make a symlink
- ebuild_symlink: str = os.path.join(tmpdir, os.path.basename(pathname))
- os.symlink(os.path.join(os.getcwd(), ebuild), ebuild_symlink)
-
- for removals, additions in changes:
- args = []
- if removals:
- for rem in removals:
- # Drop leading '~' and '-' characters and prepend '^'
- i = 1 if rem[0] in ('~', '-') else 0
- args.append('^' + rem[i:])
- if additions:
- args.extend(additions)
- args.append(ebuild_symlink)
-
- result = ekeyword.main(args)
- if result != 0:
- break
-
- return result
-
-
-def main(argv: Sequence[str]) -> int:
- if len(argv) != 4:
- return -1
-
- O = argv[0] # %O - filename of original
- A = argv[1] # %A - filename of our current version
- B = argv[2] # %B - filename of the other branch's version
- P = argv[3] # %P - original path of the file
- # Get changes to KEYWORDS= from %O to %B
- if changes := keyword_changes(O, B):
- # Apply %O -> %B changes to %A
- result = apply_keyword_changes(A, P, changes)
- return result
- # Get changes to KEYWORDS= from %O to %A
- elif changes := keyword_changes(O, A):
- # Apply %O -> %A changes to %B
- result = apply_keyword_changes(B, P, changes)
- # Merged file should be left in %A
- shutil.move(B, A)
- return result
- else:
- try:
- os.execlp("git", "git", "merge-file", "-L", "HEAD", "-L", "base", "-L", "ours", A, O, B)
- except OSError:
- return -1
+from gentoolkit.merge_driver_ekeyword import merge_driver_ekeyword
-if __name__ == "__main__":
- sys.exit(main(sys.argv[1:]))
+sys.exit(merge_driver_ekeyword.main(sys.argv[1:]))
diff --git a/pym/gentoolkit/merge_driver_ekeyword/__init__.py b/pym/gentoolkit/merge_driver_ekeyword/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pym/gentoolkit/merge_driver_ekeyword/__init__.py
diff --git a/pym/gentoolkit/merge_driver_ekeyword/merge_driver_ekeyword.py b/pym/gentoolkit/merge_driver_ekeyword/merge_driver_ekeyword.py
new file mode 100755
index 0000000..59527bb
--- /dev/null
+++ b/pym/gentoolkit/merge_driver_ekeyword/merge_driver_ekeyword.py
@@ -0,0 +1,143 @@
+#!/usr/bin/python3
+#
+# Copyright 2020-2024 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2 or later
+
+"""
+Custom git merge driver for handling conflicts in KEYWORDS assignments
+
+See https://git-scm.com/docs/gitattributes#_defining_a_custom_merge_driver
+"""
+
+import difflib
+import os
+import shutil
+import sys
+import tempfile
+
+from typing import Optional
+from collections.abc import Sequence
+
+from gentoolkit.ekeyword import ekeyword
+
+
+KeywordChanges = list[tuple[Optional[list[str]], Optional[list[str]]]]
+
+
+def keyword_array(keyword_line: str) -> list[str]:
+ # Find indices of string inside the double-quotes
+ i1: int = keyword_line.find('"') + 1
+ i2: int = keyword_line.rfind('"')
+
+ # Split into array of KEYWORDS
+ return keyword_line[i1:i2].split(' ')
+
+
+def keyword_line_changes(old: str, new: str) -> KeywordChanges:
+ a: list[str] = keyword_array(old)
+ b: list[str] = keyword_array(new)
+
+ s = difflib.SequenceMatcher(a=a, b=b)
+
+ changes: KeywordChanges = []
+ for tag, i1, i2, j1, j2 in s.get_opcodes():
+ if tag == 'replace':
+ changes.append((a[i1:i2], b[j1:j2]),)
+ elif tag == 'delete':
+ changes.append((a[i1:i2], None),)
+ elif tag == 'insert':
+ changes.append((None, b[j1:j2]),)
+ else:
+ assert tag == 'equal'
+ return changes
+
+
+def keyword_changes(ebuild1: str, ebuild2: str) -> Optional[KeywordChanges]:
+ with open(ebuild1) as e1, open(ebuild2) as e2:
+ lines1 = e1.readlines()
+ lines2 = e2.readlines()
+
+ diff = difflib.unified_diff(lines1, lines2, n=0)
+ assert next(diff) == '--- \n'
+ assert next(diff) == '+++ \n'
+
+ hunk: int = 0
+ old: str = ''
+ new: str = ''
+
+ for line in diff:
+ if line.startswith('@@ '):
+ if hunk > 0:
+ break
+ hunk += 1
+ elif line.startswith('-'):
+ if old or new:
+ break
+ old = line
+ elif line.startswith('+'):
+ if not old or new:
+ break
+ new = line
+ else:
+ if 'KEYWORDS=' in old and 'KEYWORDS=' in new:
+ return keyword_line_changes(old, new)
+ return None
+
+
+def apply_keyword_changes(ebuild: str, pathname: str,
+ changes: KeywordChanges) -> int:
+ result: int = 0
+
+ with tempfile.TemporaryDirectory() as tmpdir:
+ # ekeyword will only modify files named *.ebuild, so make a symlink
+ ebuild_symlink: str = os.path.join(tmpdir, os.path.basename(pathname))
+ os.symlink(os.path.join(os.getcwd(), ebuild), ebuild_symlink)
+
+ for removals, additions in changes:
+ args = []
+ if removals:
+ for rem in removals:
+ # Drop leading '~' and '-' characters and prepend '^'
+ i = 1 if rem[0] in ('~', '-') else 0
+ args.append('^' + rem[i:])
+ if additions:
+ args.extend(additions)
+ args.append(ebuild_symlink)
+
+ result = ekeyword.main(args)
+ if result != 0:
+ break
+
+ return result
+
+
+def main(argv: Sequence[str]) -> int:
+ if len(argv) != 4:
+ return -1
+
+ O = argv[0] # %O - filename of original
+ A = argv[1] # %A - filename of our current version
+ B = argv[2] # %B - filename of the other branch's version
+ P = argv[3] # %P - original path of the file
+
+ # Get changes to KEYWORDS= from %O to %B
+ if changes := keyword_changes(O, B):
+ # Apply %O -> %B changes to %A
+ result = apply_keyword_changes(A, P, changes)
+ return result
+ # Get changes to KEYWORDS= from %O to %A
+ elif changes := keyword_changes(O, A):
+ # Apply %O -> %A changes to %B
+ result = apply_keyword_changes(B, P, changes)
+ # Merged file should be left in %A
+ shutil.move(B, A)
+ return result
+ else:
+ try:
+ os.execlp("git", "git", "merge-file", "-L", "HEAD", "-L", "base", "-L", "ours", A, O, B)
+ except OSError:
+ return -1
+
+
+if __name__ == "__main__":
+ sys.exit(main(sys.argv[1:]))