def apply_diff(text, diff, reverse=False, verify=True): """ SOME EXAMPLES OF diff #@@ -1 +1 @@ #-before china goes live, the content team will have to manually update the settings for the china-ready apps currently in marketplace. #+before china goes live (end January developer release, June general audience release) , the content team will have to manually update the settings for the china-ready apps currently in marketplace. @@ -0,0 +1,3 @@ +before china goes live, the content team will have to manually update the settings for the china-ready apps currently in marketplace. + +kward has the details. @@ -1 +1 @@ -before china goes live (end January developer release, June general audience release), the content team will have to manually update the settings for the china-ready apps currently in marketplace. +before china goes live , the content team will have to manually update the settings for the china-ready apps currently in marketplace. @@ -3 +3 ,6 @@ -kward has the details.+kward has the details. + +Target Release Dates : +https://mana.mozilla.org/wiki/display/PM/Firefox+OS+Wave+Launch+Cross+Functional+View + +Content Team Engagement & Tasks : https://appreview.etherpad.mozilla.org/40 """ if not diff: return text output = text hunks = [ (new_diff[start_hunk], new_diff[start_hunk + 1:end_hunk]) for new_diff in [[ d.lstrip() for d in diff if d.lstrip() and d != "\\ No newline at end of file" ] + ["@@"]] # ANOTHER REPAIR for start_hunk, end_hunk in pairwise( i for i, l in enumerate(new_diff) if l.startswith("@@")) ] for header, hunk_body in reversed(hunks) if reverse else hunks: matches = DIFF_PREFIX.match(header.strip()) if not matches: if not _Log: _late_import() _Log.error("Can not handle \n---\n{{diff}}\n---\n", diff=diff) removes = tuple(int(i.strip()) for i in matches.group(1).split( ",")) # EXPECTING start_line, length TO REMOVE remove = Data( start=removes[0], length=1 if len(removes) == 1 else removes[1]) # ASSUME FIRST LINE adds = tuple(int(i.strip()) for i in matches.group(2).split( ",")) # EXPECTING start_line, length TO ADD add = Data(start=adds[0], length=1 if len(adds) == 1 else adds[1]) if add.length == 0 and add.start == 0: add.start = remove.start def repair_hunk(hunk_body): # THE LAST DELETED LINE MAY MISS A "\n" MEANING THE FIRST # ADDED LINE WILL BE APPENDED TO THE LAST DELETED LINE # EXAMPLE: -kward has the details.+kward has the details. # DETECT THIS PROBLEM FOR THIS HUNK AND FIX THE DIFF if reverse: last_lines = [ o for b, o in zip(reversed(hunk_body), reversed(output)) if b != "+" + o ] if not last_lines: return hunk_body last_line = last_lines[0] for problem_index, problem_line in enumerate(hunk_body): if problem_line.startswith("-") and problem_line.endswith( "+" + last_line): split_point = len(problem_line) - (len(last_line) + 1) break elif problem_line.startswith("+" + last_line + "-"): split_point = len(last_line) + 1 break else: return hunk_body else: if not output: return hunk_body last_line = output[-1] for problem_index, problem_line in enumerate(hunk_body): if problem_line.startswith("+") and problem_line.endswith( "-" + last_line): split_point = len(problem_line) - (len(last_line) + 1) break elif problem_line.startswith("-" + last_line + "+"): split_point = len(last_line) + 1 break else: return hunk_body new_hunk_body = ( hunk_body[:problem_index] + [problem_line[:split_point], problem_line[split_point:]] + hunk_body[problem_index + 1:]) return new_hunk_body hunk_body = repair_hunk(hunk_body) if reverse: new_output = (output[:add.start - 1] + [d[1:] for d in hunk_body if d and d[0] == "-"] + output[add.start + add.length - 1:]) else: new_output = (output[:add.start - 1] + [d[1:] for d in hunk_body if d and d[0] == "+"] + output[add.start + remove.length - 1:]) output = new_output if verify: original = apply_diff(output, diff, not reverse, False) if set(text) != set(original): # bugzilla-etl diffs are a jumble for t, o in zip_longest(text, original): if t in ["reports: https://goo.gl/70o6w6\r"]: break # KNOWN INCONSISTENCIES if t != o: if not _Log: _late_import() _Log.error("logical verification check failed") break return output
def apply_diff(text, diff, reverse=False, verify=True): """ SOME EXAMPLES OF diff #@@ -1 +1 @@ #-before china goes live, the content team will have to manually update the settings for the china-ready apps currently in marketplace. #+before china goes live (end January developer release, June general audience release) , the content team will have to manually update the settings for the china-ready apps currently in marketplace. @@ -0,0 +1,3 @@ +before china goes live, the content team will have to manually update the settings for the china-ready apps currently in marketplace. + +kward has the details. @@ -1 +1 @@ -before china goes live (end January developer release, June general audience release), the content team will have to manually update the settings for the china-ready apps currently in marketplace. +before china goes live , the content team will have to manually update the settings for the china-ready apps currently in marketplace. @@ -3 +3 ,6 @@ -kward has the details.+kward has the details. + +Target Release Dates : +https://mana.mozilla.org/wiki/display/PM/Firefox+OS+Wave+Launch+Cross+Functional+View + +Content Team Engagement & Tasks : https://appreview.etherpad.mozilla.org/40 """ output = text if not diff: return output start_of_hunk = 0 while True: if start_of_hunk >= len(diff): break header = diff[start_of_hunk] start_of_hunk += 1 if not header.strip(): continue matches = DIFF_PREFIX.match(header.strip()) if not matches: if not _Log: _late_import() _Log.error("Can not handle \n---\n{{diff}}\n---\n", diff=diff) remove = tuple(int(i.strip()) for i in matches.group(1).split( ",")) # EXPECTING start_line, length TO REMOVE remove = Data( start=remove[0], length=1 if len(remove) == 1 else remove[1]) # ASSUME FIRST LINE add = tuple(int(i.strip()) for i in matches.group(2).split( ",")) # EXPECTING start_line, length TO ADD add = Data(start=add[0], length=1 if len(add) == 1 else add[1]) if remove.start == 0 and remove.length == 0: remove.start = add.start if add.start == 0 and add.length == 0: add.start = remove.start if remove.start != add.start: if not _Log: _late_import() _Log.warning("Do not know how to handle") def repair_hunk(diff): # THE LAST DELETED LINE MAY MISS A "\n" MEANING THE FIRST # ADDED LINE WILL BE APPENDED TO THE LAST DELETED LINE # EXAMPLE: -kward has the details.+kward has the details. # DETECT THIS PROBLEM FOR THIS HUNK AND FIX THE DIFF problem_line = diff[start_of_hunk + remove.length - 1] if reverse: if add.length == 0: return diff first_added_line = output[add.start - 1] if problem_line.endswith('+' + first_added_line): split_point = len(problem_line) - len(first_added_line) - 1 else: return diff else: if remove.length == 0: return diff last_removed_line = output[remove.start - 1] if problem_line.startswith('-' + last_removed_line + "+"): split_point = len(last_removed_line) + 1 else: return diff new_diff = ( diff[:start_of_hunk + remove.length - 1] + [problem_line[:split_point], problem_line[split_point:]] + diff[start_of_hunk + remove.length:]) return new_diff diff = repair_hunk(diff) diff = [d for d in diff if d != "\\ no newline at end of file"] # ANOTHER REPAIR if reverse: new_output = (output[:add.start - 1] + [ d[1:] for d in diff[start_of_hunk:start_of_hunk + remove.length] ] + output[add.start + add.length - 1:]) else: # APPLYING DIFF FORWARD REQUIRES WE APPLY THE HUNKS IN REVERSE TO GET THE LINE NUMBERS RIGHT? new_output = (output[:remove.start - 1] + [ d[1:] for d in diff[start_of_hunk + remove.length:start_of_hunk + remove.length + add.length] ] + output[remove.start + remove.length - 1:]) start_of_hunk += remove.length + add.length output = new_output if verify: original = apply_diff(output, diff, not reverse, False) if any(t != o for t, o in zip_longest(text, original)): if not _Log: _late_import() _Log.error("logical verification check failed") return output
def apply_diff(text, diff, reverse=False, verify=True): """ SOME EXAMPLES OF diff #@@ -1 +1 @@ #-before china goes live, the content team will have to manually update the settings for the china-ready apps currently in marketplace. #+before china goes live (end January developer release, June general audience release) , the content team will have to manually update the settings for the china-ready apps currently in marketplace. @@ -0,0 +1,3 @@ +before china goes live, the content team will have to manually update the settings for the china-ready apps currently in marketplace. + +kward has the details. @@ -1 +1 @@ -before china goes live (end January developer release, June general audience release), the content team will have to manually update the settings for the china-ready apps currently in marketplace. +before china goes live , the content team will have to manually update the settings for the china-ready apps currently in marketplace. @@ -3 +3 ,6 @@ -kward has the details.+kward has the details. + +Target Release Dates : +https://mana.mozilla.org/wiki/display/PM/Firefox+OS+Wave+Launch+Cross+Functional+View + +Content Team Engagement & Tasks : https://appreview.etherpad.mozilla.org/40 """ if not diff: return text output = text hunks = [ (new_diff[start_hunk], new_diff[start_hunk+1:end_hunk]) for new_diff in [[d.lstrip() for d in diff if d.lstrip() and d != "\\ No newline at end of file"] + ["@@"]] # ANOTHER REPAIR for start_hunk, end_hunk in pairwise(i for i, l in enumerate(new_diff) if l.startswith('@@')) ] for header, hunk_body in (reversed(hunks) if reverse else hunks): matches = DIFF_PREFIX.match(header.strip()) if not matches: if not _Log: _late_import() _Log.error("Can not handle \n---\n{{diff}}\n---\n", diff=diff) removes = tuple(int(i.strip()) for i in matches.group(1).split(",")) # EXPECTING start_line, length TO REMOVE remove = Data(start=removes[0], length=1 if len(removes) == 1 else removes[1]) # ASSUME FIRST LINE adds = tuple(int(i.strip()) for i in matches.group(2).split(",")) # EXPECTING start_line, length TO ADD add = Data(start=adds[0], length=1 if len(adds) == 1 else adds[1]) if add.length == 0 and add.start == 0: add.start = remove.start def repair_hunk(hunk_body): # THE LAST DELETED LINE MAY MISS A "\n" MEANING THE FIRST # ADDED LINE WILL BE APPENDED TO THE LAST DELETED LINE # EXAMPLE: -kward has the details.+kward has the details. # DETECT THIS PROBLEM FOR THIS HUNK AND FIX THE DIFF if reverse: last_lines = [ o for b, o in zip(reversed(hunk_body), reversed(output)) if b != "+" + o ] if not last_lines: return hunk_body last_line = last_lines[0] for problem_index, problem_line in enumerate(hunk_body): if problem_line.startswith('-') and problem_line.endswith('+' + last_line): split_point = len(problem_line) - (len(last_line) + 1) break elif problem_line.startswith('+' + last_line + "-"): split_point = len(last_line) + 1 break else: return hunk_body else: if not output: return hunk_body last_line = output[-1] for problem_index, problem_line in enumerate(hunk_body): if problem_line.startswith('+') and problem_line.endswith('-' + last_line): split_point = len(problem_line) - (len(last_line) + 1) break elif problem_line.startswith('-' + last_line + "+"): split_point = len(last_line) + 1 break else: return hunk_body new_hunk_body = ( hunk_body[:problem_index] + [problem_line[:split_point], problem_line[split_point:]] + hunk_body[problem_index + 1:] ) return new_hunk_body hunk_body = repair_hunk(hunk_body) if reverse: new_output = ( output[:add.start - 1] + [d[1:] for d in hunk_body if d and d[0] == '-'] + output[add.start + add.length - 1:] ) else: new_output = ( output[:add.start - 1] + [d[1:] for d in hunk_body if d and d[0] == '+'] + output[add.start + remove.length - 1:] ) output = new_output if verify: original = apply_diff(output, diff, not reverse, False) if set(text) != set(original): # bugzilla-etl diffs are a jumble for t, o in zip_longest(text, original): if t in ['reports: https://goo.gl/70o6w6\r']: break # KNOWN INCONSISTENCIES if t != o: if not _Log: _late_import() _Log.error("logical verification check failed") break return output