The idea is from Kamesh. Attached is the patch which I think implements the idea in a way but with an issue.
First, the patch: Log [[[ If diff command does not exist on the system then use difflib.unified_diff() to generate diff contents. * tools/hook-scripts/mailer/mailer.py: Fall back to difflib.unified_diff() if the script fails to execute native 'diff' command. Patch by: Noorul Islam K M <noorul{_AT_}collab.net> Suggested by: Kamesh Jayachandran <kamesh{_AT_}collab.net> ]]] Now the issue: difflib.unified_diff() produces a bit different output from that of 'diff' command. The attached python script can be used to generate diffs using difflib. I did try different combinations of creating patches and applying patches, all of them worked fine. I am not sure whether this difference in output can be ignored. Thanks and Regards Noorul
Index: hook-scripts/mailer/mailer.py =================================================================== --- hook-scripts/mailer/mailer.py (revision 1031409) +++ hook-scripts/mailer/mailer.py (working copy) @@ -866,12 +866,16 @@ content = src_fname = dst_fname = None else: src_fname, dst_fname = diff.get_files() - content = DiffContent(self.cfg.get_diff_cmd(self.group, { - 'label_from' : label1, - 'label_to' : label2, - 'from' : src_fname, - 'to' : dst_fname, - })) + try: + content = DiffContent(self.cfg.get_diff_cmd(self.group, { + 'label_from' : label1, + 'label_to' : label2, + 'from' : src_fname, + 'to' : dst_fname, + })) + except OSError: + # diff command does not exist, try difflib.unified_diff() + content = DifflibDiffContent(label1, label2, src_fname, dst_fname) # return a data item for this diff return _data( @@ -946,7 +950,57 @@ type=ltype, ) +class DifflibDiffContent(): + "This is a generator-like object returning annotated lines of a diff." + def __init__(self, label_from, label_to, from_file, to_file): + import difflib + self.seen_change = False + fromlines = open(from_file, 'U').readlines() + tolines = open(to_file, 'U').readlines() + self.diff = difflib.unified_diff(fromlines, tolines, + label_from, label_to) + + def __nonzero__(self): + # we always have some items + return True + + def __getitem__(self, idx): + + try: + line = self.diff.next() + except StopIteration: + raise IndexError + + # classify the type of line. + first = line[:1] + if first == '@': + self.seen_change = True + ltype = 'H' + elif first == '-': + if self.seen_change: + ltype = 'D' + else: + ltype = 'F' + elif first == '+': + if self.seen_change: + ltype = 'A' + else: + ltype = 'T' + elif first == ' ': + ltype = 'C' + else: + ltype = 'U' + + if line[-2] == '\r': + line=line[0:-2] + '\n' # remove carriage return + + return _data( + raw=line, + text=line[1:-1], # remove indicator and newline + type=ltype, + ) + class TextCommitRenderer: "This class will render the commit mail in plain text."
#!/usr/bin/python import sys from difflib import unified_diff if len(sys.argv) < 3: sys.exit(1) from_file = sys.argv[1] to_file = sys.argv[2] f1 = open(from_file, 'U').readlines() f2 = open(to_file, 'U').readlines() diff = unified_diff(f1, f2, from_file, to_file) for line in diff: sys.stdout.write(line)