@@ -778,7 +778,7 @@ def build_log(sha, options)
778778 end
779779
780780 if !current_path ||
781- commit_touches_path? ( c , current_path , options [ :follow ] )
781+ commit_touches_path? ( c , current_path , options [ :follow ] , walker )
782782
783783 # This is a commit we care about, unless we haven't skipped enough
784784 # yet
@@ -792,36 +792,43 @@ def build_log(sha, options)
792792 commits
793793 end
794794
795- # Returns true if the given commit affects the given path. If the
796- # +follow+ option is true and the file specified by +path+ was renamed,
797- # then the path value is set to the old path.
798- def commit_touches_path? ( commit , path , follow )
799- if follow
800- touches_path_diff? ( commit , path )
801- else
802- touches_path_tree? ( commit , path )
803- end
804- end
805-
806795 # Returns true if +commit+ introduced changes to +path+, using commit
807- # trees to make that determination.
808- def touches_path_tree? ( commit , path )
809- parent = commit . parents [ 0 ]
796+ # trees to make that determination. Uses the history simplification
797+ # rules that `git log` uses by default, where a commit is omitted if it
798+ # is TREESAME to any parent.
799+ #
800+ # If the +follow+ option is true and the file specified by +path+ was
801+ # renamed, then the path value is set to the old path.
802+ def commit_touches_path? ( commit , path , follow , walker )
810803 entry = tree_entry ( commit , path )
811804
812- if parent . nil ?
805+ if commit . parents . empty ?
813806 # This is the root commit, return true if it has +path+ in its tree
814807 return entry != nil
815808 end
816809
817- parent_entry = tree_entry ( parent , path )
810+ num_treesame = 0
811+ commit . parents . each do |parent |
812+ parent_entry = tree_entry ( parent , path )
813+
814+ # Only follow the first TREESAME parent for merge commits
815+ if num_treesame > 0
816+ walker . hide ( parent )
817+ next
818+ end
818819
819- if entry . nil? && parent_entry . nil?
820- false
821- elsif entry . nil? || parent_entry . nil?
820+ if entry . nil? && parent_entry . nil?
821+ num_treesame += 1
822+ elsif entry && parent_entry && entry [ :oid ] == parent_entry [ :oid ]
823+ num_treesame += 1
824+ end
825+ end
826+
827+ case num_treesame
828+ when 0
829+ detect_rename ( commit , commit . parents . first , path ) if follow
822830 true
823- else
824- entry [ :oid ] != parent_entry [ :oid ]
831+ else false
825832 end
826833 end
827834
@@ -841,24 +848,18 @@ def tree_entry(commit, path)
841848 tmp_entry
842849 end
843850
844- # Returns true if +commit+ introduced changes to +path+, using
845- # Rugged::Diff objects to make that determination. This is slower than
846- # comparing commit trees, but lets us use Rugged::Diff#find_similar to
847- # detect file renames.
848- def touches_path_diff? ( commit , path )
849- diff = commit . diff ( reverse : true , paths : [ path ] ,
850- disable_pathspec_match : true )
851-
852- return false if diff . deltas . empty?
851+ # Compare +commit+ and +parent+ for +path+. If +path+ is a file and was
852+ # renamed in +commit+, then set +path+ to the old filename.
853+ def detect_rename ( commit , parent , path )
854+ diff = parent . diff ( commit , paths : [ path ] , disable_pathspec_match : true )
853855
854856 # If +path+ is a filename, not a directory, then we should only have
855857 # one delta. We don't need to follow renames for directories.
856- return true if diff . deltas . length > 1
858+ return nil if diff . deltas . length > 1
857859
858- # Detect renames
859860 delta = diff . deltas . first
860861 if delta . added?
861- full_diff = commit . diff ( reverse : true )
862+ full_diff = parent . diff ( commit )
862863 full_diff . find_similar!
863864
864865 full_diff . each_delta do |full_delta |
@@ -868,8 +869,6 @@ def touches_path_diff?(commit, path)
868869 end
869870 end
870871 end
871-
872- true
873872 end
874873
875874 def archive_to_file ( treeish = 'master' , prefix = nil , filename = 'archive.tar.gz' , format = nil , compress_cmd = %W( gzip ) )
0 commit comments