Added per-tag functionality (needs better formatting)

This commit is contained in:
Matthew 2021-08-20 17:20:50 -04:00
parent a57d09a60d
commit 74d6f64333
4 changed files with 278 additions and 69 deletions

View File

@ -123,7 +123,7 @@ class ChangesThread(threading.Thread):
def run(self):
git_log_r = subprocess.Popen(filter(None, ["git", "log", "--reverse", "--pretty=%ct|%cd|%H|%aN|%aE",
"--stat=100000,8192", "--no-merges", "-w", interval.get_since(),
"--stat=100000,8192", "-w", interval.get_since(),
interval.get_until(), "--date=short"] + (["-C", "-C", "-M"] if self.hard else []) +
[self.first_hash + self.second_hash]), bufsize=1, stdout=subprocess.PIPE).stdout
lines = git_log_r.readlines()
@ -182,56 +182,110 @@ class Changes(object):
authors_by_email = {}
emails_by_author = {}
def __init__(self, repo, hard):
self.commits = []
interval.set_ref("HEAD");
git_rev_list_p = subprocess.Popen(filter(None, ["git", "rev-list", "--reverse", "--no-merges",
def __init__(self, repo, hard, tag, branch):
if (tag != ""):
self.commits = []
interval.set_ref("HEAD");
git_rev_list_p = subprocess.Popen(filter(None, ["git", "rev-list", "--reverse",
interval.get_since(), interval.get_until(), tag]), bufsize=1,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
lines = git_rev_list_p.communicate()[0].splitlines()
git_rev_list_p.stdout.close()
if git_rev_list_p.returncode == 0 and len(lines) > 0:
progress_text = _(PROGRESS_TEXT)
if repo != None:
progress_text = "[%s] " % repo.name + progress_text
chunks = len(lines) // CHANGES_PER_THREAD
self.commits = [None] * (chunks if len(lines) % CHANGES_PER_THREAD == 0 else chunks + 1)
first_hash = ""
for i, entry in enumerate(lines):
if i % CHANGES_PER_THREAD == CHANGES_PER_THREAD - 1:
entry = entry.decode("utf-8", "replace").strip()
second_hash = entry
ChangesThread.create(hard, self, first_hash, second_hash, i)
first_hash = entry + ".."
if format.is_interactive_format():
terminal.output_progress(progress_text, i, len(lines))
else:
if CHANGES_PER_THREAD - 1 != i % CHANGES_PER_THREAD:
entry = entry.decode("utf-8", "replace").strip()
second_hash = entry
ChangesThread.create(hard, self, first_hash, second_hash, i)
# Make sure all threads have completed.
for i in range(0, NUM_THREADS):
__thread_lock__.acquire()
# We also have to release them for future use.
for i in range(0, NUM_THREADS):
__thread_lock__.release()
self.commits = [item for sublist in self.commits for item in sublist]
if len(self.commits) > 0:
if interval.has_interval():
interval.set_ref(self.commits[-1].sha)
self.first_commit_date = datetime.date(int(self.commits[0].date[0:4]), int(self.commits[0].date[5:7]),
int(self.commits[0].date[8:10]))
self.last_commit_date = datetime.date(int(self.commits[-1].date[0:4]), int(self.commits[-1].date[5:7]),
int(self.commits[-1].date[8:10]))
elif (branch != ""):
asdad
else:
self.commits = []
interval.set_ref("HEAD");
git_rev_list_p = subprocess.Popen(filter(None, ["git", "rev-list", "--reverse", "--no-merges",
interval.get_since(), interval.get_until(), "HEAD"]), bufsize=1,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
lines = git_rev_list_p.communicate()[0].splitlines()
git_rev_list_p.stdout.close()
lines = git_rev_list_p.communicate()[0].splitlines()
git_rev_list_p.stdout.close()
if git_rev_list_p.returncode == 0 and len(lines) > 0:
progress_text = _(PROGRESS_TEXT)
if repo != None:
progress_text = "[%s] " % repo.name + progress_text
if git_rev_list_p.returncode == 0 and len(lines) > 0:
progress_text = _(PROGRESS_TEXT)
if repo != None:
progress_text = "[%s] " % repo.name + progress_text
chunks = len(lines) // CHANGES_PER_THREAD
self.commits = [None] * (chunks if len(lines) % CHANGES_PER_THREAD == 0 else chunks + 1)
first_hash = ""
chunks = len(lines) // CHANGES_PER_THREAD
self.commits = [None] * (chunks if len(lines) % CHANGES_PER_THREAD == 0 else chunks + 1)
first_hash = ""
for i, entry in enumerate(lines):
if i % CHANGES_PER_THREAD == CHANGES_PER_THREAD - 1:
entry = entry.decode("utf-8", "replace").strip()
second_hash = entry
ChangesThread.create(hard, self, first_hash, second_hash, i)
first_hash = entry + ".."
for i, entry in enumerate(lines):
if i % CHANGES_PER_THREAD == CHANGES_PER_THREAD - 1:
entry = entry.decode("utf-8", "replace").strip()
second_hash = entry
ChangesThread.create(hard, self, first_hash, second_hash, i)
first_hash = entry + ".."
if format.is_interactive_format():
terminal.output_progress(progress_text, i, len(lines))
if format.is_interactive_format():
terminal.output_progress(progress_text, i, len(lines))
else:
if CHANGES_PER_THREAD - 1 != i % CHANGES_PER_THREAD:
entry = entry.decode("utf-8", "replace").strip()
second_hash = entry
ChangesThread.create(hard, self, first_hash, second_hash, i)
# Make sure all threads have completed.
for i in range(0, NUM_THREADS):
__thread_lock__.acquire()
# Make sure all threads have completed.
for i in range(0, NUM_THREADS):
__thread_lock__.acquire()
# We also have to release them for future use.
for i in range(0, NUM_THREADS):
__thread_lock__.release()
# We also have to release them for future use.
for i in range(0, NUM_THREADS):
__thread_lock__.release()
self.commits = [item for sublist in self.commits for item in sublist]
self.commits = [item for sublist in self.commits for item in sublist]
if len(self.commits) > 0:
if interval.has_interval():
interval.set_ref(self.commits[-1].sha)
if len(self.commits) > 0:
if interval.has_interval():
interval.set_ref(self.commits[-1].sha)
self.first_commit_date = datetime.date(int(self.commits[0].date[0:4]), int(self.commits[0].date[5:7]),
self.first_commit_date = datetime.date(int(self.commits[0].date[0:4]), int(self.commits[0].date[5:7]),
int(self.commits[0].date[8:10]))
self.last_commit_date = datetime.date(int(self.commits[-1].date[0:4]), int(self.commits[-1].date[5:7]),
self.last_commit_date = datetime.date(int(self.commits[-1].date[0:4]), int(self.commits[-1].date[5:7]),
int(self.commits[-1].date[8:10]))
def __iadd__(self, other):

View File

@ -23,6 +23,7 @@ import atexit
import getopt
import os
import sys
import subprocess
from .blame import Blame
from .changes import Changes
from .config import GitConfig
@ -37,6 +38,7 @@ from .output.filteringoutput import FilteringOutput
from .output.metricsoutput import MetricsOutput
from .output.responsibilitiesoutput import ResponsibilitiesOutput
from .output.responsibility_per_file_output import Responsibility_Per_File_Output
from .output.changes_per_tag_output import Changes_Per_Tag_Output
from .output.timelineoutput import TimelineOutput
localization.init()
@ -66,52 +68,108 @@ class Runner(object):
terminal.skip_escapes(not sys.stdout.isatty())
terminal.set_stdout_encoding()
previous_directory = os.getcwd()
summed_blames = Blame.__new__(Blame)
summed_changes = Changes.__new__(Changes)
summed_blames = []
summed_changes = []
tags = []
branches = []
summed_metrics = MetricsLogic.__new__(MetricsLogic)
for repo in repos:
os.chdir(repo.location)
repo = repo if len(repos) > 1 else None
changes = Changes(repo, self.hard)
summed_blames += Blame(repo, self.hard, self.useweeks, changes)
summed_changes += changes
if self.include_metrics:
summed_metrics += MetricsLogic()
if sys.stdout.isatty() and format.is_interactive_format():
terminal.clear_row()
else:
os.chdir(previous_directory)
if self.per_tag:
os.chdir(repo.location)
repo = repo if len(repos) > 1 else None
#git list of tags
process = subprocess.Popen(['git', 'tag'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
tags = process.communicate()[0].splitlines()
for tag in tags:
changes = Changes(repo, self.hard, tag, "")
summed_blames.append(Blame(repo, self.hard, self.useweeks, changes))
summed_changes.append(changes)
elif self.per_branch:
os.chdir(repo.location)
repo = repo if len(repos) > 1 else None
#get list of branches
for branch in branches:
changes = Changes(repo, self.hard, "", branch)
summed_blames.append(Blame(repo, self.hard, self.useweeks, changes))
summed_changes.append(changes)
else:
os.chdir(repo.location)
repo = repo if len(repos) > 1 else None
summed_blames = Blame.__new__(Blame)
summed_changes = Changes.__new__(Changes)
changes = Changes(repo, self.hard, False, False)
summed_blames += Blame(repo, self.hard, self.useweeks, changes)
summed_changes += changes
if self.include_metrics:
summed_metrics += MetricsLogic()
if sys.stdout.isatty() and format.is_interactive_format():
terminal.clear_row()
else:
os.chdir(previous_directory)
format.output_header(repos)
if (self.suppress == False):
outputable.output(ChangesOutput(summed_changes))
if self.per_tag:
i = 0
while (i < len(summed_changes)):
if (self.suppress == False):
outputable.output(Changes_Per_Tag_Output(summed_changes[i], tags[i]))
if summed_changes[i].get_commits():
if (self.suppress == False or self.blame == True):
outputable.output(BlameOutput(summed_changes[i], summed_blames[i]))
if self.timeline:
outputable.output(TimelineOutput(summed_changes[i], self.useweeks))
if summed_changes.get_commits():
if self.blame:
outputable.output(BlameOutput(summed_changes, summed_blames))
if self.include_metrics:
outputable.output(MetricsOutput(summed_metrics))
if self.responsibilities:
outputable.output(ResponsibilitiesOutput(summed_changes[i], summed_blames[i]))
if self.per_file:
outputable.output(Responsibility_Per_File_Output(summed_changes[i], summed_blames[i]))
if self.timeline:
outputable.output(TimelineOutput(summed_changes, self.useweeks))
outputable.output(FilteringOutput())
if self.list_file_types:
outputable.output(ExtensionsOutput())
if self.include_metrics:
outputable.output(MetricsOutput(summed_metrics))
format.output_footer()
os.chdir(previous_directory)
i = i + 1
else:
if (self.suppress == False):
outputable.output(ChangesOutput(summed_changes))
if self.responsibilities:
outputable.output(ResponsibilitiesOutput(summed_changes, summed_blames))
if summed_changes.get_commits():
if (self.suppress == False or self.blame == True):
outputable.output(BlameOutput(summed_changes, summed_blames))
if self.per_file:
outputable.output(Responsibility_Per_File_Output(summed_changes, summed_blames))
if self.timeline:
outputable.output(TimelineOutput(summed_changes, self.useweeks))
outputable.output(FilteringOutput())
if self.include_metrics:
outputable.output(MetricsOutput(summed_metrics))
if self.list_file_types:
outputable.output(ExtensionsOutput())
if self.responsibilities:
outputable.output(ResponsibilitiesOutput(summed_changes, summed_blames))
format.output_footer()
os.chdir(previous_directory)
if self.per_file:
outputable.output(Responsibility_Per_File_Output(summed_changes, summed_blames))
outputable.output(FilteringOutput())
if self.list_file_types:
outputable.output(ExtensionsOutput())
format.output_footer()
os.chdir(previous_directory)
def __check_python_version__():
if sys.version_info < (2, 6):
@ -147,7 +205,8 @@ def main():
opts, args = optval.gnu_getopt(argv[1:], "f:F:hHlLmrTwxb:", ["exclude=", "file-types=", "format=",
"hard:true", "help", "list-file-types:true", "localize-output:true",
"metrics:true", "responsibilities:true", "since=", "grading:true",
"timeline:true", "until=", "version", "weeks:true", "per-file:true", "blame:true", "suppress:false"])
"timeline:true", "until=", "version", "weeks:true", "per-file:true", "blame:true", "suppress:false",
"per-branch:false", "per-tag:false"])
repos = __get_validated_git_repos__(set(args))
#We need the repos above to be set before we read the git config.

View File

@ -0,0 +1,96 @@
# coding: utf-8
#
# Copyright © 2012-2015 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
# gitinspector is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gitinspector is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import unicode_literals
import json
import textwrap
from ..localization import N_
from .. import format, gravatar, terminal
from .outputable import Outputable
HISTORICAL_INFO_TEXT = N_("The following historical commit information, by author, for this tag:was found")
NO_COMMITED_FILES_TEXT = N_("No commited files with the specified extensions were found")
class Changes_Per_Tag_Output(Outputable):
def __init__(self, changes, tag):
self.changes = changes
self.tag = tag
Outputable.__init__(self)
def output_json(self):
authorinfo_list = self.changes.get_authorinfo_list()
total_changes = 0.0
for i in authorinfo_list:
total_changes += authorinfo_list.get(i).insertions
total_changes += authorinfo_list.get(i).deletions
if authorinfo_list:
message_json = "\t\t\t\"message\": \"" + "The following historical commit information, by author, for this tag:" + self.tag + " was found" + "\",\n"
changes_json = ""
for i in sorted(authorinfo_list):
author_email = self.changes.get_latest_email_by_author(i)
authorinfo = authorinfo_list.get(i)
percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
name_json = "\t\t\t\t\"name\": \"" + i + "\",\n"
email_json = "\t\t\t\t\"email\": \"" + author_email + "\",\n"
gravatar_json = "\t\t\t\t\"gravatar\": \"" + gravatar.get_url(author_email) + "\",\n"
commits_json = "\t\t\t\t\"commits\": " + str(authorinfo.commits) + ",\n"
insertions_json = "\t\t\t\t\"insertions\": " + str(authorinfo.insertions) + ",\n"
deletions_json = "\t\t\t\t\"deletions\": " + str(authorinfo.deletions) + ",\n"
percentage_json = "\t\t\t\t\"percentage_of_changes\": " + "{0:.2f}".format(percentage) + "\n"
changes_json += ("{\n" + name_json + email_json + gravatar_json + commits_json +
insertions_json + deletions_json + percentage_json + "\t\t\t}")
changes_json += ","
else:
changes_json = changes_json[:-1]
print("\t\t\"changes\": {\n" + message_json + "\t\t\t\"authors\": [\n\t\t\t" + changes_json + "]\n\t\t}", end="")
else:
print("\t\t\"exception\": \"" + _(NO_COMMITED_FILES_TEXT) + "\"")
def output_text(self, tag):
authorinfo_list = self.changes.get_authorinfo_list()
total_changes = 0.0
for i in authorinfo_list:
total_changes += authorinfo_list.get(i).insertions
total_changes += authorinfo_list.get(i).deletions
if authorinfo_list:
print(textwrap.fill("The following historical commit information, by author, for this tag:" + self.tag + " was found" + ":", width=terminal.get_size()[0]) + "\n")
terminal.printb(terminal.ljust(_("Author"), 21) + terminal.rjust(_("Commits"), 13) +
terminal.rjust(_("Insertions"), 14) + terminal.rjust(_("Deletions"), 15) +
terminal.rjust(_("% of changes"), 16))
for i in sorted(authorinfo_list):
authorinfo = authorinfo_list.get(i)
percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
print(terminal.ljust(i, 20)[0:20 - terminal.get_excess_column_count(i)], end=" ")
print(str(authorinfo.commits).rjust(13), end=" ")
print(str(authorinfo.insertions).rjust(13), end=" ")
print(str(authorinfo.deletions).rjust(14), end=" ")
print("{0:.2f}".format(percentage).rjust(15))
else:
print(_(NO_COMMITED_FILES_TEXT) + ".")

View File

@ -72,7 +72,7 @@ class Responsibility_Per_File_Output(Outputable):
resp_json += "\t\t\t\t\t\"author name\": \"" + author_dict["author_name"] + "\",\n"
resp_json += "\t\t\t\t\t\"author email\": \"" + author_dict["author_email"] + "\", \n"
resp_json += "\t\t\t\t\t\"rows owned\": " + str(author_dict["rows_owned"]) + ",\n"
resp_json += "\t\t\t\t\t\"perctenage of document\": " + str(float(author_dict["rows_owned"])/float(total_rows_dict[file])) + "\n"
resp_json += "\t\t\t\t\t\"perctenage of document\": " + str(float(author_dict["rows_owned"])/float(total_rows_dict[file])*100) + "\n"
resp_json += "\t\t\t\t},"
#next 3 lines are diddo from responsibilitiesoutput.py