Support for multiple repositories is near completion (See issue #24).
Only the metrics module is lacking support. While the rest should work, please note that it is completely untested as of now and probably has some rough edges.
This commit is contained in:
parent
46b21db196
commit
98615ccbfc
|
@ -148,6 +148,17 @@ class Blame(object):
|
||||||
for i in range(0, NUM_THREADS):
|
for i in range(0, NUM_THREADS):
|
||||||
__thread_lock__.acquire()
|
__thread_lock__.acquire()
|
||||||
|
|
||||||
|
# We also have to release them for future use.
|
||||||
|
for i in range(0, NUM_THREADS):
|
||||||
|
__thread_lock__.release()
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
if other == None:
|
||||||
|
return self
|
||||||
|
|
||||||
|
self.blames.update(other.blames)
|
||||||
|
return self
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_revision(string):
|
def is_revision(string):
|
||||||
revision = re.search("([0-9a-f]{40})", string)
|
revision = re.search("([0-9a-f]{40})", string)
|
||||||
|
@ -180,12 +191,3 @@ class Blame(object):
|
||||||
summed_blames[i[0][0]].comments += i[1].comments
|
summed_blames[i[0][0]].comments += i[1].comments
|
||||||
|
|
||||||
return summed_blames
|
return summed_blames
|
||||||
|
|
||||||
__blame__ = None
|
|
||||||
|
|
||||||
def get(hard, useweeks, changes):
|
|
||||||
global __blame__
|
|
||||||
if __blame__ == None:
|
|
||||||
__blame__ = Blame(hard, useweeks, changes)
|
|
||||||
|
|
||||||
return __blame__
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import atexit
|
||||||
import getopt
|
import getopt
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from .blame import Blame
|
||||||
from .changes import Changes
|
from .changes import Changes
|
||||||
from .config import GitConfig
|
from .config import GitConfig
|
||||||
from . import (basedir, clone, extensions, filtering, format, help, interval,
|
from . import (basedir, clone, extensions, filtering, format, help, interval,
|
||||||
|
@ -58,13 +59,16 @@ class Runner(object):
|
||||||
terminal.skip_escapes(not sys.stdout.isatty())
|
terminal.skip_escapes(not sys.stdout.isatty())
|
||||||
terminal.set_stdout_encoding()
|
terminal.set_stdout_encoding()
|
||||||
previous_directory = os.getcwd()
|
previous_directory = os.getcwd()
|
||||||
changes = None
|
summed_blames = None
|
||||||
|
summed_changes = None
|
||||||
|
|
||||||
for repo in repos:
|
for repo in repos:
|
||||||
os.chdir(previous_directory)
|
os.chdir(previous_directory)
|
||||||
os.chdir(repo)
|
os.chdir(repo)
|
||||||
absolute_path = basedir.get_basedir_git()
|
absolute_path = basedir.get_basedir_git()
|
||||||
changes = Changes(self.hard) + changes
|
changes = Changes(self.hard)
|
||||||
|
summed_blames = Blame(self.hard, self.useweeks, changes) + summed_blames
|
||||||
|
summed_changes = changes + summed_changes
|
||||||
|
|
||||||
if sys.stdout.isatty() and format.is_interactive_format():
|
if sys.stdout.isatty() and format.is_interactive_format():
|
||||||
terminal.clear_row()
|
terminal.clear_row()
|
||||||
|
@ -75,16 +79,16 @@ class Runner(object):
|
||||||
outputable.output(ChangesOutput(changes))
|
outputable.output(ChangesOutput(changes))
|
||||||
|
|
||||||
if changes.get_commits():
|
if changes.get_commits():
|
||||||
outputable.output(BlameOutput(changes, self.hard, self.useweeks))
|
outputable.output(BlameOutput(summed_changes, summed_blames))
|
||||||
|
|
||||||
if self.timeline:
|
if self.timeline:
|
||||||
outputable.output(TimelineOutput(changes, self.useweeks))
|
outputable.output(TimelineOutput(summed_changes, self.useweeks))
|
||||||
|
|
||||||
if self.include_metrics:
|
if self.include_metrics:
|
||||||
outputable.output(MetricsOutput())
|
outputable.output(MetricsOutput())
|
||||||
|
|
||||||
if self.responsibilities:
|
if self.responsibilities:
|
||||||
outputable.output(ResponsibilitiesOutput(changes, self.hard, self.useweeks))
|
outputable.output(ResponsibilitiesOutput(summed_changes, summed_blames))
|
||||||
|
|
||||||
outputable.output(FilteringOutput())
|
outputable.output(FilteringOutput())
|
||||||
|
|
||||||
|
|
|
@ -23,21 +23,20 @@ import json
|
||||||
import sys
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
from ..localization import N_
|
from ..localization import N_
|
||||||
from .. import blame, format, gravatar, terminal
|
from .. import format, gravatar, terminal
|
||||||
|
from ..blame import Blame
|
||||||
from .outputable import Outputable
|
from .outputable import Outputable
|
||||||
|
|
||||||
BLAME_INFO_TEXT = N_("Below are the number of rows from each author that have survived and are still "
|
BLAME_INFO_TEXT = N_("Below are the number of rows from each author that have survived and are still "
|
||||||
"intact in the current revision")
|
"intact in the current revision")
|
||||||
|
|
||||||
class BlameOutput(Outputable):
|
class BlameOutput(Outputable):
|
||||||
def __init__(self, changes, hard, useweeks):
|
def __init__(self, changes, blame):
|
||||||
if format.is_interactive_format():
|
if format.is_interactive_format():
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
self.changes = changes
|
self.changes = changes
|
||||||
self.hard = hard
|
self.blame = blame
|
||||||
self.useweeks = useweeks
|
|
||||||
blame.get(self.hard, self.useweeks, self.changes)
|
|
||||||
Outputable.__init__(self)
|
Outputable.__init__(self)
|
||||||
|
|
||||||
def output_html(self):
|
def output_html(self):
|
||||||
|
@ -47,7 +46,7 @@ class BlameOutput(Outputable):
|
||||||
_("Author"), _("Rows"), _("Stability"), _("Age"), _("% in comments"))
|
_("Author"), _("Rows"), _("Stability"), _("Age"), _("% in comments"))
|
||||||
blame_xml += "<tbody>"
|
blame_xml += "<tbody>"
|
||||||
chart_data = ""
|
chart_data = ""
|
||||||
blames = sorted(blame.__blame__.get_summed_blames().items())
|
blames = sorted(self.blame.get_summed_blames().items())
|
||||||
total_blames = 0
|
total_blames = 0
|
||||||
|
|
||||||
for i in blames:
|
for i in blames:
|
||||||
|
@ -64,7 +63,7 @@ class BlameOutput(Outputable):
|
||||||
blame_xml += "<td>" + entry[0] + "</td>"
|
blame_xml += "<td>" + entry[0] + "</td>"
|
||||||
|
|
||||||
blame_xml += "<td>" + str(entry[1].rows) + "</td>"
|
blame_xml += "<td>" + str(entry[1].rows) + "</td>"
|
||||||
blame_xml += "<td>" + ("{0:.1f}".format(blame.Blame.get_stability(entry[0], entry[1].rows, self.changes)) + "</td>")
|
blame_xml += "<td>" + ("{0:.1f}".format(Blame.get_stability(entry[0], entry[1].rows, self.changes)) + "</td>")
|
||||||
blame_xml += "<td>" + "{0:.1f}".format(float(entry[1].skew) / entry[1].rows) + "</td>"
|
blame_xml += "<td>" + "{0:.1f}".format(float(entry[1].skew) / entry[1].rows) + "</td>"
|
||||||
blame_xml += "<td>" + "{0:.2f}".format(100.0 * entry[1].comments / entry[1].rows) + "</td>"
|
blame_xml += "<td>" + "{0:.2f}".format(100.0 * entry[1].comments / entry[1].rows) + "</td>"
|
||||||
blame_xml += "<td style=\"display: none\">" + work_percentage + "</td>"
|
blame_xml += "<td style=\"display: none\">" + work_percentage + "</td>"
|
||||||
|
@ -99,13 +98,13 @@ class BlameOutput(Outputable):
|
||||||
message_xml = "\t\t\t\"message\": \"" + _(BLAME_INFO_TEXT) + "\",\n"
|
message_xml = "\t\t\t\"message\": \"" + _(BLAME_INFO_TEXT) + "\",\n"
|
||||||
blame_xml = ""
|
blame_xml = ""
|
||||||
|
|
||||||
for i in sorted(blame.__blame__.get_summed_blames().items()):
|
for i in sorted(self.blame.get_summed_blames().items()):
|
||||||
author_email = self.changes.get_latest_email_by_author(i[0])
|
author_email = self.changes.get_latest_email_by_author(i[0])
|
||||||
|
|
||||||
name_xml = "\t\t\t\t\"name\": \"" + i[0] + "\",\n"
|
name_xml = "\t\t\t\t\"name\": \"" + i[0] + "\",\n"
|
||||||
gravatar_xml = "\t\t\t\t\"gravatar\": \"" + gravatar.get_url(author_email) + "\",\n"
|
gravatar_xml = "\t\t\t\t\"gravatar\": \"" + gravatar.get_url(author_email) + "\",\n"
|
||||||
rows_xml = "\t\t\t\t\"rows\": " + str(i[1].rows) + ",\n"
|
rows_xml = "\t\t\t\t\"rows\": " + str(i[1].rows) + ",\n"
|
||||||
stability_xml = ("\t\t\t\t\"stability\": " + "{0:.1f}".format(blame.Blame.get_stability(i[0], i[1].rows,
|
stability_xml = ("\t\t\t\t\"stability\": " + "{0:.1f}".format(Blame.get_stability(i[0], i[1].rows,
|
||||||
self.changes)) + ",\n")
|
self.changes)) + ",\n")
|
||||||
age_xml = ("\t\t\t\t\"age\": " + "{0:.1f}".format(float(i[1].skew) / i[1].rows) + ",\n")
|
age_xml = ("\t\t\t\t\"age\": " + "{0:.1f}".format(float(i[1].skew) / i[1].rows) + ",\n")
|
||||||
percentage_in_comments_xml = ("\t\t\t\t\"percentage-in-comments\": " + "{0:.2f}".format(100.0 * i[1].comments / i[1].rows) +
|
percentage_in_comments_xml = ("\t\t\t\t\"percentage-in-comments\": " + "{0:.2f}".format(100.0 * i[1].comments / i[1].rows) +
|
||||||
|
@ -125,10 +124,10 @@ class BlameOutput(Outputable):
|
||||||
terminal.printb(terminal.ljust(_("Author"), 21) + terminal.rjust(_("Rows"), 10) + terminal.rjust(_("Stability"), 15) +
|
terminal.printb(terminal.ljust(_("Author"), 21) + terminal.rjust(_("Rows"), 10) + terminal.rjust(_("Stability"), 15) +
|
||||||
terminal.rjust(_("Age"), 13) + terminal.rjust(_("% in comments"), 20))
|
terminal.rjust(_("Age"), 13) + terminal.rjust(_("% in comments"), 20))
|
||||||
|
|
||||||
for i in sorted(blame.__blame__.get_summed_blames().items()):
|
for i in sorted(self.blame.get_summed_blames().items()):
|
||||||
print(terminal.ljust(i[0], 20)[0:20 - terminal.get_excess_column_count(i[0])], end=" ")
|
print(terminal.ljust(i[0], 20)[0:20 - terminal.get_excess_column_count(i[0])], end=" ")
|
||||||
print(str(i[1].rows).rjust(10), end=" ")
|
print(str(i[1].rows).rjust(10), end=" ")
|
||||||
print("{0:.1f}".format(blame.Blame.get_stability(i[0], i[1].rows, self.changes)).rjust(14), end=" ")
|
print("{0:.1f}".format(Blame.get_stability(i[0], i[1].rows, self.changes)).rjust(14), end=" ")
|
||||||
print("{0:.1f}".format(float(i[1].skew) / i[1].rows).rjust(12), end=" ")
|
print("{0:.1f}".format(float(i[1].skew) / i[1].rows).rjust(12), end=" ")
|
||||||
print("{0:.2f}".format(100.0 * i[1].comments / i[1].rows).rjust(19))
|
print("{0:.2f}".format(100.0 * i[1].comments / i[1].rows).rjust(19))
|
||||||
|
|
||||||
|
@ -136,13 +135,13 @@ class BlameOutput(Outputable):
|
||||||
message_xml = "\t\t<message>" + _(BLAME_INFO_TEXT) + "</message>\n"
|
message_xml = "\t\t<message>" + _(BLAME_INFO_TEXT) + "</message>\n"
|
||||||
blame_xml = ""
|
blame_xml = ""
|
||||||
|
|
||||||
for i in sorted(blame.__blame__.get_summed_blames().items()):
|
for i in sorted(self.blame.get_summed_blames().items()):
|
||||||
author_email = self.changes.get_latest_email_by_author(i[0])
|
author_email = self.changes.get_latest_email_by_author(i[0])
|
||||||
|
|
||||||
name_xml = "\t\t\t\t<name>" + i[0] + "</name>\n"
|
name_xml = "\t\t\t\t<name>" + i[0] + "</name>\n"
|
||||||
gravatar_xml = "\t\t\t\t<gravatar>" + gravatar.get_url(author_email) + "</gravatar>\n"
|
gravatar_xml = "\t\t\t\t<gravatar>" + gravatar.get_url(author_email) + "</gravatar>\n"
|
||||||
rows_xml = "\t\t\t\t<rows>" + str(i[1].rows) + "</rows>\n"
|
rows_xml = "\t\t\t\t<rows>" + str(i[1].rows) + "</rows>\n"
|
||||||
stability_xml = ("\t\t\t\t<stability>" + "{0:.1f}".format(blame.Blame.get_stability(i[0], i[1].rows,
|
stability_xml = ("\t\t\t\t<stability>" + "{0:.1f}".format(Blame.get_stability(i[0], i[1].rows,
|
||||||
self.changes)) + "</stability>\n")
|
self.changes)) + "</stability>\n")
|
||||||
age_xml = ("\t\t\t\t<age>" + "{0:.1f}".format(float(i[1].skew) / i[1].rows) + "</age>\n")
|
age_xml = ("\t\t\t\t<age>" + "{0:.1f}".format(float(i[1].skew) / i[1].rows) + "</age>\n")
|
||||||
percentage_in_comments_xml = ("\t\t\t\t<percentage-in-comments>" + "{0:.2f}".format(100.0 * i[1].comments / i[1].rows) +
|
percentage_in_comments_xml = ("\t\t\t\t<percentage-in-comments>" + "{0:.2f}".format(100.0 * i[1].comments / i[1].rows) +
|
||||||
|
|
|
@ -21,7 +21,7 @@ from __future__ import print_function
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import textwrap
|
import textwrap
|
||||||
from ..localization import N_
|
from ..localization import N_
|
||||||
from .. import blame, format, gravatar, terminal
|
from .. import format, gravatar, terminal
|
||||||
from .. import responsibilities as resp
|
from .. import responsibilities as resp
|
||||||
from .outputable import Outputable
|
from .outputable import Outputable
|
||||||
|
|
||||||
|
@ -31,18 +31,17 @@ RESPONSIBILITIES_INFO_TEXT = N_("The following responsibilities, by author, were
|
||||||
MOSTLY_RESPONSIBLE_FOR_TEXT = N_("is mostly responsible for")
|
MOSTLY_RESPONSIBLE_FOR_TEXT = N_("is mostly responsible for")
|
||||||
|
|
||||||
class ResponsibilitiesOutput(Outputable):
|
class ResponsibilitiesOutput(Outputable):
|
||||||
def __init__(self, changes, hard, useweeks):
|
def __init__(self, changes, blame):
|
||||||
self.changes = changes
|
self.changes = changes
|
||||||
self.hard = hard
|
self.blame = blame
|
||||||
self.useweeks = useweeks
|
|
||||||
Outputable.__init__(self)
|
Outputable.__init__(self)
|
||||||
|
|
||||||
def output_text(self):
|
def output_text(self):
|
||||||
print("\n" + textwrap.fill(_(RESPONSIBILITIES_INFO_TEXT) + ":", width=terminal.get_size()[0]))
|
print("\n" + textwrap.fill(_(RESPONSIBILITIES_INFO_TEXT) + ":", width=terminal.get_size()[0]))
|
||||||
|
|
||||||
for i in sorted(set(i[0] for i in blame.get(self.hard, self.useweeks, self.changes).blames)):
|
for i in sorted(set(i[0] for i in self.blame.blames)):
|
||||||
responsibilities = sorted(((i[1], i[0]) for i in resp.Responsibilities.get(self.changes,
|
responsibilities = sorted(((i[1], i[0]) for i in resp.Responsibilities.get(self.changes,
|
||||||
self.hard, self.useweeks, i)), reverse=True)
|
self.blame, i)), reverse=True)
|
||||||
if responsibilities:
|
if responsibilities:
|
||||||
print("\n" + i, _(MOSTLY_RESPONSIBLE_FOR_TEXT) + ":")
|
print("\n" + i, _(MOSTLY_RESPONSIBLE_FOR_TEXT) + ":")
|
||||||
|
|
||||||
|
@ -60,9 +59,9 @@ class ResponsibilitiesOutput(Outputable):
|
||||||
resp_xml = "<div><div class=\"box\" id=\"responsibilities\">"
|
resp_xml = "<div><div class=\"box\" id=\"responsibilities\">"
|
||||||
resp_xml += "<p>" + _(RESPONSIBILITIES_INFO_TEXT) + ".</p>"
|
resp_xml += "<p>" + _(RESPONSIBILITIES_INFO_TEXT) + ".</p>"
|
||||||
|
|
||||||
for i in sorted(set(i[0] for i in blame.get(self.hard, self.useweeks, self.changes).blames)):
|
for i in sorted(set(i[0] for i in self.blame.blames)):
|
||||||
responsibilities = sorted(((i[1], i[0]) for i in resp.Responsibilities.get(self.changes,
|
responsibilities = sorted(((i[1], i[0]) for i in resp.Responsibilities.get(self.changes,
|
||||||
self.hard, self.useweeks, i)), reverse=True)
|
self.blame, i)), reverse=True)
|
||||||
if responsibilities:
|
if responsibilities:
|
||||||
resp_xml += "<div>"
|
resp_xml += "<div>"
|
||||||
|
|
||||||
|
@ -87,9 +86,9 @@ class ResponsibilitiesOutput(Outputable):
|
||||||
message_xml = "\t\t\t\"message\": \"" + _(RESPONSIBILITIES_INFO_TEXT) + "\",\n"
|
message_xml = "\t\t\t\"message\": \"" + _(RESPONSIBILITIES_INFO_TEXT) + "\",\n"
|
||||||
resp_xml = ""
|
resp_xml = ""
|
||||||
|
|
||||||
for i in sorted(set(i[0] for i in blame.get(self.hard, self.useweeks, self.changes).blames)):
|
for i in sorted(set(i[0] for i in self.blame.blames)):
|
||||||
responsibilities = sorted(((i[1], i[0]) for i in resp.Responsibilities.get(self.changes,
|
responsibilities = sorted(((i[1], i[0]) for i in resp.Responsibilities.get(self.changes,
|
||||||
self.hard, self.useweeks, i)), reverse=True)
|
self.blame, i)), reverse=True)
|
||||||
if responsibilities:
|
if responsibilities:
|
||||||
author_email = self.changes.get_latest_email_by_author(i)
|
author_email = self.changes.get_latest_email_by_author(i)
|
||||||
|
|
||||||
|
@ -118,9 +117,9 @@ class ResponsibilitiesOutput(Outputable):
|
||||||
message_xml = "\t\t<message>" + _(RESPONSIBILITIES_INFO_TEXT) + "</message>\n"
|
message_xml = "\t\t<message>" + _(RESPONSIBILITIES_INFO_TEXT) + "</message>\n"
|
||||||
resp_xml = ""
|
resp_xml = ""
|
||||||
|
|
||||||
for i in sorted(set(i[0] for i in blame.get(self.hard, self.useweeks, self.changes).blames)):
|
for i in sorted(set(i[0] for i in self.blame.blames)):
|
||||||
responsibilities = sorted(((i[1], i[0]) for i in resp.Responsibilities.get(self.changes,
|
responsibilities = sorted(((i[1], i[0]) for i in resp.Responsibilities.get(self.changes,
|
||||||
self.hard, self.useweeks, i)), reverse=True)
|
self.blame, i)), reverse=True)
|
||||||
if responsibilities:
|
if responsibilities:
|
||||||
author_email = self.changes.get_latest_email_by_author(i)
|
author_email = self.changes.get_latest_email_by_author(i)
|
||||||
|
|
||||||
|
|
|
@ -26,10 +26,10 @@ class ResponsibiltyEntry(object):
|
||||||
|
|
||||||
class Responsibilities(object):
|
class Responsibilities(object):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get(changes, hard, useweeks, author_name):
|
def get(changes, blame, author_name):
|
||||||
author_blames = {}
|
author_blames = {}
|
||||||
|
|
||||||
for i in blame.get(hard, useweeks, changes).blames.items():
|
for i in blame.blames.items():
|
||||||
if author_name == i[0][0]:
|
if author_name == i[0][0]:
|
||||||
total_rows = i[1].rows - i[1].comments
|
total_rows = i[1].rows - i[1].comments
|
||||||
if total_rows > 0:
|
if total_rows > 0:
|
||||||
|
|
Loading…
Reference in New Issue