Skip to content

Instantly share code, notes, and snippets.

@lanmaster53
Last active September 18, 2015 09:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save lanmaster53/4461a4853ba0741b2a29 to your computer and use it in GitHub Desktop.
Save lanmaster53/4461a4853ba0741b2a29 to your computer and use it in GitHub Desktop.
#!/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