#!/usr/local/bin/ruby # == Synopsis # # overlaps: graph files that are edited by multiple users per day using a stem and leaf plot # # == Usage # # overlaps [ -p port ] [ -u user ] ( [ -s date ] | [ -w weeks ] | [ -d date ] ) [ -e date ] [ -v ] [ -m minimum ] # # Flagged files may be candidates for splitting into multiple files or refactoring # # * -s Start date. Incompatible with -w. One week before end date by default. # * -e End date. Today by default. # * -w Number of weeks before end date to go examine. # * -v Show detailed list of overlapping files. # * -d Date. Gives overlaps for a single day. Overrides all other date related flags. # * -m Minimum number of overlaps required to be flagged. 2 by default. $:.unshift File.join( File.dirname( $0 ), "p4ruby", RUBY_PLATFORM ) require 'date' require 'P4' require 'getoptlong' require 'rdoc/usage' def getOverLappingChanges( date, showDetails = false ) today = date tomorrow = date + 1 fileDict = {} # get the changes for the day changes = $p4.run( "changes", "-s", "submitted", $path + '@>' + today.strftime( "%Y/%m/%d" ) + ',@<' + tomorrow.strftime( "%Y/%m/%d" ) ) # run 'describe' on each change to get the list of files and user changes.each do | c | files = $p4.run( "describe", c["change"] ); if( files[0]["depotFile"] == nil ) next end # lazily rely on hashes to determine uniquenss of file path and users files[0]["depotFile"].each do |f| if( !fileDict.has_key?( f ) ) fileDict[ f ] = {} end fileDict[ f ].merge!( { c["user"] => 1 } ) end end # start building up our plot string result = today.strftime( '%Y/%m/%d' ) + "\t" # sort by count for stem and leaf plot so that large overlaps # don't get lost in the forest of numbers oc = fileDict.sort {|x,y| y[1].keys.length<=>x[1].keys.length} oc.each{ |f| if( f[1].keys.length >= $min ) result += f[1].keys.length.to_s() end } puts result # sort detailed output by path so that related files are adjacent oc = fileDict.sort {|x,y| x[0] <=> y[0]} if( showDetails ) oc.each{ |f| if( f[1].keys.length >= $min ) puts f[1].keys.length.to_s() + "\t\t" + f[0] end } end end begin # unreasonable defaults port = "" user = "" weeks = 1 $min = 2 details = false last = start = Date.today $path = "//..." # get the command line options if any, overriding the defaults opts = GetoptLong.new( [ '--help', '-h', GetoptLong::NO_ARGUMENT ], [ '--user', '-u', GetoptLong::REQUIRED_ARGUMENT ], [ '--port', '-p', GetoptLong::REQUIRED_ARGUMENT ], [ '--date', '-d', GetoptLong::REQUIRED_ARGUMENT ], [ '--weeks', '-w', GetoptLong::REQUIRED_ARGUMENT ], [ '--min', '-m', GetoptLong::REQUIRED_ARGUMENT ], [ '--start', '-s', GetoptLong::REQUIRED_ARGUMENT ], [ '--end', '-e', GetoptLong::REQUIRED_ARGUMENT ], [ '--details', '-v', GetoptLong::NO_ARGUMENT ] ) optDict = {} opts.each{ | opt, arg | optDict[ opt ] = arg } optDict.each do |opt, arg| case opt when '--help' RDoc::usage when '--user' user = arg when '--port' port = arg when '--date' last = start = Date.parse( arg ) when '--weeks' weeks = arg.to_i() when '--min' $min = arg.to_i() when '--details' details = true when '--start' start = Date.parse( arg ) when '--end' last = Date.parse( arg ) end end if( !optDict.has_key?( '--start' ) && !optDict.has_key?( '--date' ) ) start = last - 7 * weeks end if( ARGV.length == 1 ) $path = ARGV[0] end # time to get the party started $p4 = P4.new() $p4.prog = "overlaps" # override environment variables if needs be if( port != "" ) $p4.port = port end if( user != "" ) $p4.user = user end $p4.connect() start.upto( last ) { |d| getOverLappingChanges( d, details ) } $p4.disconnect end