#!/usr/bin/python3
"""
pymergechanges: Merge commits made with changes package into text
Copyright (C) 2018 Y. Cui
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
pymergechanges: Merge commits made with changes package into text
Requires Python version 3; Tested on Python 3.6.6
Supported commands: added, deleted, replaced, highlight
Usage: python pyMergeChanges.py [-arh]
will be overwritten and must be different than .
Options:
-a: accept all added, deleted and replaced
-r: reject all added, deleted and replaced
-h: remove all highlights
If no option is given, runs interactively.
Created on Wed Dec 5 20:28:40 2018
Revised on Tue Aug 27 17:51:58 2019
"""
import sys
import re
import codecs
def parse_param(parstr):
if all(p in parstr for p in ('a', 'r')):
print('You cannot accept and reject at the same time.')
sys.exit(1)
parout = ''
for par in parstr[1:]:
if par == 'a':
print('Accepting all added, deleted and replaced')
parout += 'a'
elif par == 'r':
print('Rejecting all added, deleted and replaced')
parout += 'r'
elif par == 'h':
print('Removing all highlights.')
parout += 'h'
else:
print('Unknown parameter: ' + par)
sys.exit(1)
if not parout:
parout = 'i'
return parout
def ask1(): # accept, reject, keep, break
if PARAMS == 'i':
while True:
ans = input('[a]ccept or [r]eject or [k]eep or [b]reak ? ').lower()
if ans == 'a':
print('Accepted.')
break
elif ans == 'r':
print('Rejected.')
break
elif ans == 'k':
print('Kept.')
break
elif ans == 'b':
print('Alright.')
break
elif any(p == 'a' for p in PARAMS):
print('Accepted.')
ans = 'a'
elif any(p == 'r' for p in PARAMS):
print('Rejected.')
ans = 'r'
else:
ans = 'k'
return ans
def ask2(): # remove, keep, break
if PARAMS == 'i':
while True:
ans = input('[r]emove or [k]eep or [b]reak ? ').lower()
if ans == 'r':
print('Removed.')
break
elif ans == 'k':
print('Kept.')
break
elif ans == 'b':
print('Alright.')
break
return ans
elif any(p == 'h' for p in PARAMS):
print('Removed.')
ans = 'r'
else:
print('Kept.')
ans = 'k'
return ans
def trim_space(line, pos): # trim two consecutive white spaces
if line[pos-1:pos+1] == ' ':
line = line[:pos] + line[pos+1:]
return line
# BEGIN OF SCRIPT
if len(sys.argv) not in [3, 4]:
print(__doc__)
sys.exit(1)
# parse input parameters
if len(sys.argv) == 3:
print('Running in interactive mode. ')
INPUTFILE, OUTPUTFILE = sys.argv[1:]
PARAMS = "i"
if len(sys.argv) == 4:
PARAMLIST, INPUTFILE, OUTPUTFILE = sys.argv[1:]
PARAMS = parse_param(PARAMLIST)
if INPUTFILE == OUTPUTFILE:
print('Input File and Output File must be different.')
sys.exit(1)
RE_ADDED = re.compile(r'(\\added)(\[[^\]]*\])?\{(([^\}]?(\{[^\}]*\})?)*)\}')
RE_DELETED = re.compile(r'(\\deleted)(\[[^\]]*\])?\{(([^\}]?(\{[^\}]*\})?)*)\}')
RE_REPLACED = re.compile(r'(\\replaced)(\[[^\]]*\])?\{(([^\}]?(\{[^\}]*\})?)*)\}\{(([^\}]?(\{[^\}]*\})?)*)\}')
RE_HIGHLIGHT = re.compile(r'(\\highlight)(\[[^\]]*\])?\{(([^\}]?(\{[^\}]*\})?)*)\}')
RE_COMMENT = re.compile(r'(\\comment)(\[[^\]]*\])?\{(([^\}]?(\{[^\}]*\})?)*)\}')
codecs.open(OUTPUTFILE, mode='w', encoding='utf8').close()
with codecs.open(INPUTFILE, mode='r', encoding='utf8') as fin, \
codecs.open(OUTPUTFILE, mode='a', encoding='utf8') as fout:
LINE_COUNT = 0
FLAG_FAST_BREAK = False # if you want to halt merging for some reason
for linein in fin:
if FLAG_FAST_BREAK:
fout.write(linein)
continue
LINE_COUNT += 1
lineout = linein
matchAdded = RE_ADDED.search(lineout)
matchDeleted = RE_DELETED.search(lineout)
matchReplaced = RE_REPLACED.search(lineout)
matchHighlight = RE_HIGHLIGHT.search(lineout)
matchComment = RE_COMMENT.search(lineout)
flagHasCommit = matchAdded or matchDeleted or matchReplaced or matchHighlight or matchComment
if flagHasCommit:
print('\n******** In Line %i :\n %s' % (LINE_COUNT, lineout))
next_pos = 0
while matchAdded:
if FLAG_FAST_BREAK:
break
print('\n** add commit ** \n' + matchAdded.group())
answer = ask1()
if answer == 'a':
lineout = (lineout[:matchAdded.start(0)]
+ matchAdded.group(3) + lineout[matchAdded.end(0):])
lineout = trim_space(lineout, matchAdded.start(0)
+ len(matchAdded.group(3)))
lineout = trim_space(lineout, matchAdded.start(0))
elif answer == 'r':
lineout = (lineout[:matchAdded.start(0)] + lineout[matchAdded.end(0):])
lineout = trim_space(lineout, matchAdded.start(0))
elif answer == 'k':
lineout = lineout
next_pos = matchAdded.end(0)
elif answer == 'b':
FLAG_FAST_BREAK = True
break
matchAdded = RE_ADDED.search(lineout, next_pos) # redo matching for updated text
next_pos = 0
matchDeleted = RE_DELETED.search(lineout, next_pos) # redo matching for (potentially) updated text
while matchDeleted:
if FLAG_FAST_BREAK:
break
print('\n** delete commit ** \n' + matchDeleted.group())
answer = ask1()
if answer == 'a':
lineout = (lineout[:matchDeleted.start(0)] + lineout[matchDeleted.end(0):])
lineout = trim_space(lineout, matchDeleted.start(0))
elif answer == 'r':
lineout = (lineout[:matchDeleted.start(0)] + matchDeleted.group(3)
+ lineout[matchDeleted.end(0):])
elif answer == 'k':
lineout = lineout
next_pos = matchDeleted.end(0)
elif answer == 'b':
FLAG_FAST_BREAK = True
break
matchDeleted = RE_DELETED.search(lineout, next_pos)
next_pos = 0
matchReplaced = RE_REPLACED.search(lineout, next_pos) # redo matching for (potentially) updated text
while matchReplaced:
if FLAG_FAST_BREAK:
break
print('\n** replace commit ** \n' + matchReplaced.group())
answer = ask1()
if answer == 'a':
lineout = (lineout[:matchReplaced.start(0)]
+ matchReplaced.group(3) + lineout[matchReplaced.end(0):])
lineout = trim_space(lineout, matchReplaced.start(0)
+ len(matchReplaced.group(3)))
lineout = trim_space(lineout, matchReplaced.start(0))
elif answer == 'r':
lineout = (lineout[:matchReplaced.start(0)]
+ matchReplaced.group(6) + lineout[matchReplaced.end(0):])
lineout = trim_space(lineout, matchReplaced.start(0)
+ len(matchReplaced.group(6)))
lineout = trim_space(lineout, matchReplaced.start(0))
elif answer == 'k':
lineout = lineout
next_pos = matchReplaced.end(0)
elif answer == 'b':
FLAG_FAST_BREAK = True
break
matchReplaced = RE_REPLACED.search(lineout, next_pos)
next_pos = 0
matchHighlight = RE_HIGHLIGHT.search(lineout, next_pos) # redo matching for (potentially) updated text
while matchHighlight:
if FLAG_FAST_BREAK:
break
print('\n** highlight commit ** \n' + matchHighlight.group())
answer = ask2()
if answer == 'r':
lineout = (lineout[:matchHighlight.start(0)]
+ matchHighlight.group(3) + lineout[matchHighlight.end(0):])
lineout = trim_space(lineout, matchHighlight.start(0)
+ len(matchHighlight.group(3)))
lineout = trim_space(lineout, matchHighlight.start(0))
elif answer == 'k':
lineout = lineout
next_pos = matchHighlight.end(0)
elif answer == 'b':
FLAG_FAST_BREAK = True
break
matchHighlight = RE_HIGHLIGHT.search(lineout, next_pos)
next_pos = 0
matchComment = RE_COMMENT.search(lineout, next_pos) # redo matching for (potentially) updated text
while matchComment:
if FLAG_FAST_BREAK:
break
print('\n** comment commit ** \n' + matchComment.group())
answer = ask2()
if answer == 'r':
lineout = (lineout[:matchComment.start(0)] + lineout[matchComment.end(0):])
lineout = trim_space(lineout, matchComment.start(0))
elif answer == 'k':
lineout = lineout
next_pos = matchComment.end(0)
elif answer == 'b':
FLAG_FAST_BREAK = True
break
matchComment = RE_COMMENT.search(lineout, next_pos)
if lineout.isspace():
print('\nResult is empty line and not stored.')
else:
print('\nResult: \n' + lineout + '\n')
fout.write(lineout)
else:
fout.write(linein)
#
# END OF SCRIPT