3 # Defines a class to build directory diff tools on.
12 # Directory comparison class.
16 def new(self
, a
, b
): # Initialize
19 # Properties that caller may change before calling self.run():
20 self
.hide
= [os
.curdir
, os
.pardir
] # Names never to be shown
21 self
.ignore
= ['RCS', 'tags'] # Names ignored in comparison
25 def run(self
): # Compare everything except common subdirectories
26 self
.a_list
= filter(dircache
.listdir(self
.a
), self
.hide
)
27 self
.b_list
= filter(dircache
.listdir(self
.b
), self
.hide
)
34 def phase1(self
): # Compute common names
45 if x
not in self
.common
:
48 def phase2(self
): # Distinguish files, directories, funnies
50 self
.common_files
= []
51 self
.common_funny
= []
54 a_path
= os
.path
.join(self
.a
, x
)
55 b_path
= os
.path
.join(self
.b
, x
)
59 a_stat
= statcache
.stat(a_path
)
61 # print 'Can\'t stat', a_path, ':', why[1]
64 b_stat
= statcache
.stat(b_path
)
66 # print 'Can\'t stat', b_path, ':', why[1]
70 a_type
= S_IFMT(a_stat
[ST_MODE
])
71 b_type
= S_IFMT(b_stat
[ST_MODE
])
73 self
.common_funny
.append(x
)
75 self
.common_dirs
.append(x
)
77 self
.common_files
.append(x
)
79 self
.common_funny
.append(x
)
81 self
.common_funny
.append(x
)
83 def phase3(self
): # Find out differences between common files
84 xx
= cmpfiles(self
.a
, self
.b
, self
.common_files
)
85 self
.same_files
, self
.diff_files
, self
.funny_files
= xx
87 def phase4(self
): # Find out differences between common subdirectories
88 # A new dircmp object is created for each common subdirectory,
89 # these are stored in a dictionary indexed by filename.
90 # The hide and ignore properties are inherited from the parent
92 for x
in self
.common_dirs
:
93 a_x
= os
.path
.join(self
.a
, x
)
94 b_x
= os
.path
.join(self
.b
, x
)
95 self
.subdirs
[x
] = newdd
= dircmp().new(a_x
, b_x
)
96 newdd
.hide
= self
.hide
97 newdd
.ignore
= self
.ignore
100 def phase4_closure(self
): # Recursively call phase4() on subdirectories
102 for x
in self
.subdirs
.keys():
103 self
.subdirs
[x
].phase4_closure()
105 def report(self
): # Print a report on the differences between a and b
106 # Assume that phases 1 to 3 have been executed
107 # Output format is purposely lousy
108 print 'diff', self
.a
, self
.b
110 print 'Only in', self
.a
, ':', self
.a_only
112 print 'Only in', self
.b
, ':', self
.b_only
114 print 'Identical files :', self
.same_files
116 print 'Differing files :', self
.diff_files
118 print 'Trouble with common files :', self
.funny_files
120 print 'Common subdirectories :', self
.common_dirs
121 if self
.common_funny
:
122 print 'Common funny cases :', self
.common_funny
124 def report_closure(self
): # Print reports on self and on subdirs
125 # If phase 4 hasn't been done, no subdir reports are printed
129 except AttributeError:
130 return # No subdirectories computed
131 for x
in self
.subdirs
.keys():
133 self
.subdirs
[x
].report_closure()
135 def report_phase4_closure(self
): # Report and do phase 4 recursively
138 for x
in self
.subdirs
.keys():
140 self
.subdirs
[x
].report_phase4_closure()
143 # Compare common files in two directories.
145 # - files that compare equal
146 # - files that compare different
147 # - funny cases (can't stat etc.)
149 def cmpfiles(a
, b
, common
):
152 res
[cmp(os
.path
.join(a
, x
), os
.path
.join(b
, x
))].append(x
)
160 # 2 for funny cases (can't stat, etc.)
164 if cmpcache
.cmp(a
, b
): return 0
170 # Remove a list item.
171 # NB: This modifies the list argument.
173 def remove(list, item
):
174 for i
in range(len(list)):
180 # Return a copy with items that occur in skip removed.
182 def filter(list, skip
):
185 if item
not in skip
: result
.append(item
)
189 # Demonstration and testing.
194 options
, args
= getopt
.getopt(sys
.argv
[1:], 'r')
195 if len(args
) <> 2: raise getopt
.error
, 'need exactly two args'
196 dd
= dircmp().new(args
[0], args
[1])
198 if ('-r', '') in options
:
199 dd
.report_phase4_closure()