@@ -20,12 +20,16 @@ def current(name)
2020 end
2121 end
2222
23+ # :reek:TooManyInstanceVariables
24+ # rubocop:disable Metrics/ClassLength
2325 class Churn
26+ # :reek:TooManyStatements
2427 def initialize ( churn_after : nil , paths : [ '.' ] )
2528 @churn_after = churn_after
2629 @paths = Array ( paths )
2730 @date = nil
2831 @stats = { }
32+ @git_root = find_git_root
2933
3034 call
3135 end
@@ -40,24 +44,53 @@ def date_of_last_commit(path)
4044
4145 private
4246
47+ # :reek:DuplicateMethodCall
48+ def find_git_root
49+ @paths . each do |path |
50+ current_path = File . expand_path ( path )
51+ while current_path != File . dirname ( current_path )
52+ return current_path if Dir . exist? ( File . join ( current_path , '.git' ) )
53+
54+ current_path = File . dirname ( current_path )
55+ end
56+ end
57+ Dir . pwd
58+ end
59+
4360 def call
4461 git_log_commands . each { |log_command | exec_git_command ( log_command ) }
4562 end
4663
4764 def exec_git_command ( command )
48- Git
49- . git ( command )
50- . split ( "\n " )
51- . reject ( &:empty? )
52- . each { |line | process_line ( line ) }
65+ # Run git command from the git repository root
66+ Dir . chdir ( @git_root ) do
67+ Git
68+ . git ( command )
69+ . split ( "\n " )
70+ . reject ( &:empty? )
71+ . each { |line | process_line ( line ) }
72+ end
5373 end
5474
5575 def git_log_commands
5676 @paths . map { |path | git_log_command ( path ) }
5777 end
5878
5979 def git_log_command ( path )
60- "log --all --date=iso --follow --format='format:date:%x09%ad' --name-status #{ after_clause } #{ path } "
80+ # Convert absolute paths to relative paths from git root
81+ relative_path = make_relative_to_git_root ( path )
82+ "log --all --date=iso --follow --format='format:date:%x09%ad' --name-status #{ after_clause } #{ relative_path } "
83+ end
84+
85+ def make_relative_to_git_root ( path )
86+ absolute_path = File . expand_path ( path )
87+ if absolute_path . start_with? ( @git_root )
88+ # Convert to relative path from git root
89+ absolute_path [ ( @git_root . length + 1 ) ..] || '.'
90+ else
91+ # If path is not within git root, use as is
92+ path
93+ end
6194 end
6295
6396 def after_clause
@@ -87,13 +120,18 @@ def process_rename(from, to)
87120 process_file ( to )
88121 end
89122
123+ # :reek:DuplicateMethodCall
90124 def filename_for_subdirectory ( filename )
91- git_path = Git . git ( 'rev-parse --show-toplevel' )
92- cd_path = Dir . pwd
93- if cd_path . length > git_path . length
94- filename = filename . sub ( /^#{ Regexp . escape ( "#{ File . basename ( cd_path ) } /" ) } / , '' )
125+ if @git_root == Dir . pwd
126+ git_path = Git . git ( 'rev-parse --show-toplevel' )
127+ cd_path = Dir . pwd
128+ if cd_path . length > git_path . length
129+ filename = filename . sub ( /^#{ Regexp . escape ( "#{ File . basename ( cd_path ) } /" ) } / , '' )
130+ end
131+ [ filename ]
132+ else
133+ filename
95134 end
96- [ filename ]
97135 end
98136
99137 def process_file ( filename )
@@ -109,10 +147,32 @@ def renames
109147 @renames ||= Renames . new
110148 end
111149
150+ # :reek:TooManyStatements
151+ # rubocop:disable Metrics/MethodLength
112152 def stats ( path )
153+ # Try the path as-is first
154+ result = @stats . fetch ( path , nil )
155+ return result if result
156+
157+ # If not found, try converting absolute path to relative path from git root
158+ absolute_path = File . expand_path ( path )
159+ if absolute_path . start_with? ( @git_root )
160+ relative_path = absolute_path [ ( @git_root . length + 1 ) ..]
161+ return @stats . fetch ( relative_path , Stats . new ( 0 ) )
162+ end
163+
164+ # If still not found, try converting relative path to absolute path
165+ unless path . start_with? ( '/' )
166+ absolute_path = File . expand_path ( path , @git_root )
167+ return @stats . fetch ( absolute_path , Stats . new ( 0 ) )
168+ end
169+
170+ # Default fallback
113171 @stats . fetch ( path , Stats . new ( 0 ) )
114172 end
173+ # rubocop:enable Metrics/MethodLength
115174 end
175+ # rubocop:enable Metrics/ClassLength
116176 end
117177 end
118178end
0 commit comments