Created
August 31, 2012 01:49
-
-
Save jlebar/3547640 to your computer and use it in GitHub Desktop.
Reorder a patch file so it's easier to review
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
"""Re-order the files in a patch according to a set of rules. | |
Input is accepted on stdin, and written to stdout. | |
Usage: cat patch | reorder-patch > reordered-patch | |
""" | |
import sys | |
import os | |
import re | |
def splitdiff(f): | |
"""Split a file containing a diff into pieces, one per file. | |
We always return a patch header as the first piece; if the given patch does | |
not have a header, we return the empty string. | |
""" | |
lines = '' | |
for line in f: | |
if line.startswith('diff'): | |
yield lines | |
lines = '' | |
lines += line | |
yield lines | |
def get_filename(piece): | |
"""Get the filename from a diff piece.""" | |
try: | |
# The filename is the last space-separated token in the first line of | |
# the piece. This is a wildly inefficient implementation. | |
return piece.split('\n')[0].split(' ')[-1] | |
except: | |
return '' | |
def compare_diff_pieces(piece_a, piece_b): | |
"""Determine whether piece_a should go above or below piece_b, according to | |
the following rules: | |
* idl come first, then ipdl files, then other files. | |
* .h files come before their corresponding .cpp files. | |
* Files with "/test/" or "/tests/" in their path come after other files. | |
* Makefiles come last in their directory. | |
* Otherwise sort in lexicographic order. | |
""" | |
a = get_filename(piece_a) | |
b = get_filename(piece_b) | |
def bool_comparator(fn): | |
"""Transform a boolean function into a comparison function over a and | |
b, such that a < b if fn(a) and not fn(b).""" | |
val_a = fn(a) | |
val_b = fn(b) | |
if val_a and not val_b: | |
return -1 | |
elif val_b and not val_a: | |
return 1 | |
return 0 | |
def comparators(): | |
# Think of these bool_comparators as: You get sorted towards the top of | |
# the patch if you match the lambda. | |
yield bool_comparator(lambda x: x.endswith('.idl')) | |
yield bool_comparator(lambda x: x.endswith('.ipdl')) | |
if a.rsplit('.', 1)[0] == b.rsplit('.', 1)[0]: | |
yield bool_comparator(lambda x: x.endswith('.h')) | |
yield bool_comparator(lambda x: not (('/test/' in x) or ('/tests/' in x))) | |
if a.rsplit('/', 1)[0] == b.rsplit('/', 1)[0]: | |
yield bool_comparator(lambda x: 'Makefile.in' not in x) | |
yield cmp(a, b) | |
for res in comparators(): | |
if res != 0: | |
return res | |
return 0 | |
def reorder(infile, outfile): | |
pieces = splitdiff(infile) | |
# outfile gets the first piece first (it's the patch header), then we sort | |
# the remaining pieces. | |
outfile.write(''.join(next(pieces))) | |
diff_pieces = list(pieces) | |
diff_pieces.sort(compare_diff_pieces) | |
outfile.write(''.join(diff_pieces)) | |
if __name__ == '__main__': | |
infile = sys.stdin | |
outfile = sys.stdout | |
reorder(infile, outfile) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Very useful for comparing svn diffs that somehow never get generated in the same order twice... Had to check for "Index" in pieces though:
https://gist.github.com/mmuman/102fd59f99898dfd6a25/03f46c5f7931bc22c416bb367d4e8938a16edd54