class Bake::Modernize::License::Authorship

Represents the authorship of a repository.

def add(path, author, time, id = nil)

Add a modification to the authorship.
def add(path, author, time, id = nil)
	modification = Modification.new(author, time, path, id)
	
	@commits[modification.key] << modification
	@paths[path] << modification
end

def copyrights

All copyrights.
def copyrights
	copyrights_for_modifications(@paths.values.flatten)
end

def copyrights_for_modifications(modifications)

All copyrights for a given modification.
def copyrights_for_modifications(modifications)
	authors = modifications.group_by{|modification| modification.full_name}
	
	authors.map do |name, modifications|
		Copyright.new(modifications.map(&:time).minmax, name)
	end.sort
end

def copyrights_for_path(path)

All copyrights for a given path.
def copyrights_for_path(path)
	copyrights_for_modifications(@paths[path])
end

def extract(root = Dir.pwd)

Extract the authorship from the given root directory.
def extract(root = Dir.pwd)
	mailmap = Mailmap.for(root)
	skip_list = SkipList.for(root)
	
	if contributors = Contributors.for(root, mailmap: mailmap)
		contributors.each do |path, author, time|
			add(path, author, time)
		end
	end
	
	walk(Rugged::Repository.discover(root), mailmap: mailmap, skip_list: skip_list)
	
	return self
end

def initialize

Create a new, empty, authorship.
def initialize
	@paths = Hash.new{|h,k| h[k] = []}
	@commits = Hash.new{|h,k| h[k] = []}
end

def sorted_authors

Authors, sorted by contribution date.
def sorted_authors
	authors = Hash.new{|h,k| h[k] = 0}
	
	@commits.each do |key, modifications|
		modifications.map(&:full_name).uniq.each do |full_name|
			authors[full_name] += 1
		end
	end
	
	return authors.sort_by{|k,v| [-v, k]}.map(&:first)
end

def walk(repository, mailmap: nil, skip_list: nil, show: "HEAD")

def walk(repository, mailmap: nil, skip_list: nil, show: "HEAD")
	Rugged::Walker.walk(repository, show: show, sort: DEFAULT_SORT) do |commit|
		next if skip_list&.ignore?(commit)
		
		diff = commit.diff
		
		# We relax the threshold for copy and rename detection because we want to detect files that have been moved and modified more generously.
		diff.find_similar!(
			rename_threshold: 25,
			copy_threshold: 25,
			ignore_whitespace: true,
		)
		
		diff.each_delta do |delta|
			old_path = delta.old_file[:path]
			new_path = delta.new_file[:path]
			
			@paths[new_path] ||= []
			
			if old_path != new_path
				# The file was moved, move copyright information too:
				Console.logger.debug(self, "Moving #{old_path} to #{new_path}", similarity: delta.similarity)
				@paths[new_path].concat(@paths[old_path])
			end
			
			author = commit.author
			
			if mailmap
				if name = mailmap.names[author[:email]]
					author[:name] = name
				end
			end
			
			add(new_path, author, commit.time, commit.oid)
		end
	end
end