utility to examine recursive differences
[infrastructure.git] / scripts / diff-rec.py
1 #!/usr/bin/python
2
3 import os, os.path
4 import subprocess
5 import re
6
7 from optparse import OptionParser
8
9 def report_stderr(stderrdata):
10     if stderrdata: 
11         print "STDERR BEG (%d chars)",len(stderrdata)
12         print stderrdata
13         print "STDERR END (%d chars)",len(stderrdata)
14
15 def git_update (dir):
16     os.chdir(dir)
17     print "git pull in %s"%dir
18     prog=subprocess.Popen(["git","pull"],stdout=subprocess.PIPE, stderr=subprocess.PIPE)
19     try:
20         (stdoutdata, stderrdata) = prog.communicate()
21     except KeyboardInterrupt:
22         raise Exception, "Interrupted by user"
23     prog.wait()
24     print stdoutdata,
25     report_stderr(stderrdata)
26
27
28 excludes=[ "*.git*", "*~" ]
29 excludes+= [ "*.pyc", "*.png", "*.pdf", "*.out" , "*.DS_Store", "*.lint" ]
30 excludes+= [ "listfiles.sh", "python.*" ]
31 def run_diff(options):
32     dir1=options.left_dir
33     dir2=options.right_dir
34     command=["diff","-r",]
35     for exclude in excludes:
36         command += [ "-x", "%s"%exclude]
37     command += [dir1,dir2]
38     prog=subprocess.Popen(command,stdout=subprocess.PIPE, stderr=subprocess.PIPE)
39     print "diffing -r %s %s"%(dir1,dir2)
40     try:
41         (stdoutdata, stderrdata) = prog.communicate()
42     except KeyboardInterrupt:
43         raise Exception, "Interrupted by user"
44     prog.wait()
45     report_stderr(stderrdata)
46
47     re_only_left =re.compile("^Only in %s[:/]"%dir1)
48     re_only_right=re.compile("^Only in %s[:/]"%dir2)
49     re_binary    =re.compile("^Binary files %s.* and %s.*"%(dir1,dir2))
50     re_start_diff=re.compile("diff -r.*%s/(?P<path>.*) %s/(?P=path)"%(dir1,dir2))
51
52     only_left=[]
53     only_right=[]
54     ignored=[]
55     
56     class Context:
57         def __init__ (self,diffs): 
58             self.current_path=''
59             self.current_diff=[]
60             self.diffs=diffs
61         def close_diff(self):
62             if self.current_path: self.diffs[self.current_path]=self.current_diff
63             elif self.current_diff: print """----------
64 WARNING, diff with no path %s
65 ----------"""%self.current_diff
66             self.current_path=''
67             self.current_diff=[]
68         def set_path (self,path):
69             self.current_path=path
70         def insert_line(self,line):
71             self.current_diff.append(line)
72
73     ### parse
74     diffs={}
75     context=Context(diffs)
76     for line in stdoutdata.split("\n"):
77         m=re_only_left.match(line)
78         if m: 
79             context.close_diff()
80             only_left.append(line)
81             continue
82         m=re_only_right.match(line)
83         if m: 
84             context.close_diff()
85             only_right.append(line)
86             continue
87         m=re_binary.match(line)
88         if m:
89             context.close_diff()
90             ignored.append(line)
91             continue
92         m=re_start_diff.match(line)
93         if m:
94             context.close_diff()
95             context.set_path(m.group('path'))
96             continue
97         context.insert_line(line)
98         
99     ### ignore some entries - maybe not needed if we're smart with diff
100         
101     # print report
102     print "< < < < < %d old files/dirs"%len(only_left)
103     for l in only_left:  print "< < < < < ",l
104     print "> > > > > %d new files/dirs"%len(only_right)
105     for l in only_right: print "> > > > > ",l
106     print "= = = = = %d files"%len(diffs)
107     for (path,contents) in diffs.iteritems():
108         if options.verbose:
109             print "= = = = =  Diffs in path %s"%path
110             for line in contents: print line
111         else:
112             print "Diffs in path %s (%d lines)"%(path,len(contents))
113             
114
115 left_default=os.path.expanduser("~/git/sfa")
116 right_default=os.path.expanduser("~/git/sfa-generic")
117
118 def main ():
119     parser=OptionParser()
120     parser.add_option("-g","--git",action='store_true',dest='git',default=False,
121                       help="run git pull before diffing")
122     parser.add_option("-l","--left",action='store',dest='left_dir',default=left_default,
123                       help="left dir, default=%s"%left_default)
124     parser.add_option("-r","--right",action='store',dest='right_dir',default=right_default,
125                       help="right dir, default=%s"%right_default)
126     parser.add_option("-v","--verbose",action='store_true',dest='verbose',default=False,
127                       help="Show actual diff contents")
128     (options,args)=parser.parse_args()
129     if options.git:
130         git_update(options.left_dir)
131         git_update(options.right_dir)
132     run_diff(options)
133
134 if __name__ == "__main__":
135     main()