Last active
September 18, 2015 09:33
-
-
Save lanmaster53/4461a4853ba0741b2a29 to your computer and use it in GitHub Desktop.
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 | |
import argparse | |
import ctypes | |
import difflib | |
import itertools | |
import os | |
import sys | |
# ingore hidden directories | |
class Colors(object): | |
N = '\033[m' # native | |
R = '\033[31m' # red | |
G = '\033[32m' # green | |
O = '\033[33m' # orange | |
B = '\033[34m' # blue | |
if os.name == 'nt': | |
N = R = G = O = B = '' | |
class Differ(object): | |
def __init__(self): | |
self.data = { | |
'missing': [], | |
'invalid': [], | |
'unknown': [], | |
'diffs': [], | |
} | |
self.skip_hidden = False | |
def compare(self, src1, src2): | |
# check validity of sources | |
if os.path.exists(src1) == False: | |
print(self.error('Source does not exist: {}'.format(src1))) | |
elif os.path.exists(src2) == False: | |
print(self.error('Source does not exist: {}'.format(src2))) | |
elif os.path.isdir(src1) == True and os.path.isdir(src2) == True: | |
self._compare_dirs(src1, src2) | |
self.report() | |
elif os.path.isfile(src1) == True and os.path.isfile(src2) == True: | |
self._compare_files(src1, src2) | |
self.report() | |
else: | |
print(self.error('Source types not consistent.')) | |
def _compare_dirs(self, dir1, dir2): | |
# walk the dir1ectory... | |
for root, dirs, files in os.walk(dir1): | |
sub1 = root.replace(dir1,'') | |
sub2 = dir2 + sub1 | |
# check to see if sub2 exists | |
if os.path.exists(sub2) == False or os.path.isdir(sub2) == False: | |
self.data['missing'].append(root) | |
for file1 in files: | |
# skip symbolic links | |
if os.path.islink(os.path.join(root, file1)): | |
continue | |
# skip hidden files | |
if self.skip_hidden == True and ishidden(os.path.join(root, file1)): | |
continue | |
# skip binary files | |
if isbinary(os.path.join(root, file1)) == True: | |
continue | |
file2 = os.path.join(sub2, file1) | |
# verify that each file in root exists in sub2 | |
if os.path.exists(file2) == False: | |
self.data['missing'].append(os.path.join(root, file1)) | |
# verify that each file in root is indeed a file in sub2 | |
elif os.path.exists(file2) == True and os.path.isfile(file2) == False: | |
self.data['invalid'].append(os.path.join(root, file1)) | |
# diff the source and target files | |
elif os.path.exists(file2) == True and os.path.isfile(file2) == True: | |
self._compare_files(os.path.join(root, file1), file2) | |
else: | |
self.data['unknown'].append(os.path.join(root, file1)) | |
def _compare_files(self, file1, file2): | |
text1 = open(file1, 'r').read().strip().split('\n') | |
text2 = open(file2, 'r').read().strip().split('\n') | |
diff = difflib.unified_diff(text1, text2, fromfile=file1, tofile=file2, lineterm='') | |
# color diff elements accordingly | |
colored = [] | |
for line in diff: | |
if line[0] == '+': | |
line = '{}{}{}'.format(Colors.G, line, Colors.N) | |
elif line[0] == '-': | |
line = '{}{}{}'.format(Colors.R, line, Colors.N) | |
colored.append(line) | |
# prepare diff for display | |
result = '\n'.join(colored) | |
if len(result) > 0: | |
self.data['diffs'].append(result) | |
def report(self): | |
count = len(list(itertools.chain(*self.data.values()))) | |
if count == 0: | |
print(self.notice('No changes between sources.')) | |
return | |
if len(self.data['missing']) > 0: | |
print('') | |
print(self.notice('Files not tracked in the target code base:')) | |
print('') | |
for _file in self.data['missing']: | |
print('\t{}'.format(_file)) | |
if len(self.data['invalid']) > 0: | |
print('') | |
print(self.notice('Files no longer valid in the target code base:')) | |
print('') | |
for _file in self.data['invalid']: | |
print('\t{}'.format(_file)) | |
if len(self.data['unknown']) > 0: | |
print('') | |
print(self.notice('Files with unknown differences:')) | |
print('') | |
for _file in self.data['unknown']: | |
print('\t{}'.format(_file)) | |
if len(self.data['diffs']) > 0: | |
if count > len(self.data['diffs']): | |
print('') | |
print(self.notice('Files with valid differences:', Colors.O)) | |
print('') | |
seperator = '*'*50 | |
print(seperator) | |
for diff in self.data['diffs']: | |
print(diff) | |
print(seperator) | |
def error(self, s): | |
return '{}[!] {}{}'.format(Colors.R, s, Colors.N) | |
def notice(self,s, color=Colors.B): | |
return '{}[*] {}{}'.format(color, s, Colors.N) | |
def ishidden(p): | |
# cross platform hidden file detection | |
if os.name == 'nt': | |
try: | |
attrs = ctypes.windll.kernel32.GetFileAttributesW(unicode(p)) | |
assert attrs != -1 | |
result = bool(attrs & 2) | |
except (AttributeError, AssertionError): | |
result = False | |
return result | |
else: | |
f = p.split(os.sep)[-1] | |
return f.startswith('.') #linux | |
def isbinary(p): | |
textchars = bytearray({7,8,9,10,12,13,27} | set(range(0x20, 0x100)) - {0x7f}) | |
return bool(open(p, 'rb').read(1024).translate(None, textchars)) | |
if __name__=='__main__': | |
parser = argparse.ArgumentParser() | |
parser.add_argument('src1', help='first source of diff') | |
parser.add_argument('src2', help='second source of diff') | |
parser.add_argument('--skip-hidden', help='skip hidden files', dest='skip_hidden', default=False, action='store_true') | |
args = parser.parse_args() | |
d = Differ() | |
d.skip_hidden = args.skip_hidden | |
d.compare(args.src1, args.src2) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment