Compare commits

...

222 Commits

Author SHA1 Message Date
Adam Waldenberg b8375442a7 Add missing changes to CHANGES.txt 2020-10-19 11:24:33 +02:00
Adam Waldenberg 6d77989e34 Fix logic error in the threading code of the changes module (Fixes #118).
This particular problem is also briefly mentioned in issue #115. The
fix resolves the overflow problem resulting in an array with a dangling
"None" type at the end. It also takes care of a race condition where
two threads by accident could update the same data.
2017-05-15 03:06:53 +02:00
Adam Waldenberg c80c822892 Ignore any unicode_escape decode fails when decoding emails (Fixes #122).
This is only needed with emails that have escape characters, as described
in issue #46. If the call fails, it will most likely not affect us and
can be safely ignored.
2017-05-13 16:41:54 +02:00
Adam Waldenberg b3fbb3e3c3 Removed dangling semicolon in changes module. 2017-05-13 16:36:05 +02:00
Bart van Andel 40cfd0ac84 Bump version for npm 2017-05-13 16:14:45 +02:00
Bart van Andel 1e5f5959db Convert line endings of Python files to Unix-style before publishing 2017-05-13 16:14:45 +02:00
Bart van Andel a8e0de7910 Shorten description so it doesn't get truncated online 2017-05-13 16:14:45 +02:00
Bart van Andel 09c5e50381 Add some more keywords 2017-05-13 16:14:45 +02:00
Bart van Andel 55434ff76a Prefer npm global install 2017-05-13 16:14:45 +02:00
Bart van Andel 3d11cdce44 Setup npm package 2017-05-13 16:14:45 +02:00
GuillermoMI 315f4079ac Fix for PHP close comment token. 2017-05-13 16:06:38 +02:00
Adam Waldenberg d0798d8358 Set ref to HEAD when starting changes analysis i (Fixes #132).
This gets rid of any invalid refs being set at a subsequent point during
execution.
2017-05-13 15:43:09 +02:00
Adam Waldenberg a56680c4b4 Handle git-rev-list and git-ls-tree with empty changesets (#132, #115).
If the changeset was empty or filtered with no matching files a bunch of
errors would be thrown from git with no proper results being returned
back to gitinspector.

We now pipe stderr (catching the output) and also check the return code
when running these commands.
2017-05-13 15:41:13 +02:00
Adam Waldenberg 8cff4bd208 Removed logically redundant part of an if statement.
The len(self.commits) > 0 check was already being performed one level up.
2017-05-13 15:41:13 +02:00
Adam Waldenberg 983d3d05bd When generating the report, summed changes need to be non-empty (#132).
Previoulsy, we were just checking against the changes returned from the
last repository analyzed - which of course broke the whole thing if that
repository had no matching commits!
2017-05-13 15:39:38 +02:00
Adam Waldenberg 9f336e63ce Faulty clone code made it possible to run gitinspector in sub-directories.
This resulted in undefined behavior. The base directory was being
incorrectly defined when cloning did not occur.
2017-05-12 15:31:28 +02:00
Adam Waldenberg ba1c3341fe The typo "defaut" should be "default" in documentation (Fixes #113). 2016-07-19 04:04:31 +02:00
Adam Waldenberg e543eeaf58 Only include valid extensions in blame output (#95). 2016-02-03 14:22:19 +01:00
Adam Waldenberg 6e0365e8ba Fix inconsistent behavior with -f "**" (#95).
Improved by taking advantage of the detected file types during the first
pass in the changes module.
2016-02-03 11:47:00 +01:00
Adam Waldenberg 139f5306f4 Better support terminals behaving like urxvt (Fixes #101).
This terminal was allowing backspaces (\b) to bypass column zero on the
current row, completely breaking the current way gitinspector was
clearing text outputted to the currently active row.
2016-01-21 00:22:52 +01:00
Adam Waldenberg 9b5bbc469f Removed superfluous quotation character in the JSON metrics output. 2015-12-18 08:41:46 +01:00
Adam Waldenberg a9d519c93b Recognize Rust programming language (.rs) comments (#83). 2015-12-17 03:15:22 +01:00
Adam Waldenberg 9aa4aba336 Removed size parameter from gravatar URL's in JSON output (#50). 2015-12-17 03:08:33 +01:00
Adam Waldenberg 6ef9936508 Fix some minor typos in the documentation.
Also regenerated the documentation using AsciiDoc.
2015-12-14 05:19:09 +01:00
Adam Waldenberg 150e316918 Add documentation about JSON output to the AsciiDoc file. 2015-12-14 05:17:11 +01:00
Adam Waldenberg ba049a0367 Fix AttributeError thrown when no extensions were detected (Fixes #91).
The bug was introduced with the commit ecc67a3 and was caused by the
initialization of an empty Changes object.

This fix makes sure that the __iadd__() method properly handles empty
change sets and returns a satisfactory object back when completing
execution.
2015-12-12 07:49:58 +01:00
Gregrs 3c48789890 Recognise Lilypond (.ly and .ily) comments 2015-12-10 07:34:18 +00:00
Adam Waldenberg 533bba64c6 Recognize comments in Robot Framework (.robot) files (#88). 2015-12-09 09:30:49 +01:00
Adam Waldenberg 4ee02f5907 Recognize Go language (.go) comments (#86). 2015-11-27 23:41:53 +01:00
Adam Waldenberg 5275521a9a Added author and license meta data to gitinspector piclet image. 2015-11-26 00:58:30 +01:00
Adam Waldenberg ecc67a31a5 Repositories are now merged with __iadd__() instead of __add__().
Thus, we are overriding the "+=" operator instead of "+". This
implementation results in a much cleaner solution as we were directly
updating "self" when overriding __add__(), something which isn't really
an acceptable solution as we were changing the supplied in-parameters.
2015-11-25 17:28:50 +01:00
Adam Waldenberg 9bd4b979b3 Fixed confusing variable names in JSON output functions and methods.
These functions and methods were originally based on the XML output.
Consequently, those variable names were also preserved. All "<name>_xml"
variables are now named "<name>_json" instead.
2015-11-24 21:20:35 +01:00
Adam Waldenberg bfde70db91 Include author email in JSON and HTML outputs (Fixes #85). 2015-11-24 21:02:32 +01:00
Adam Waldenberg 12f8c8a192 Use repos_string instead of repos[0].name in JSON output in format.py. 2015-11-07 05:55:14 +01:00
Adam Waldenberg 9a22763e64 Added missing line break in format.__get_zip_file_content__(...). 2015-11-07 05:52:23 +01:00
Adam Waldenberg 346645d655 The format.__output_html_template__(...) function now cleans up. 2015-11-07 05:51:40 +01:00
Adam Waldenberg 91d94446a7 The logo in the HTML output now links to the gitinspector page. 2015-11-04 05:56:52 +01:00
Adam Waldenberg 88d840dd51 Gitinspector now has a better and more clear logo. 2015-11-04 05:31:24 +01:00
Adam Waldenberg 26f77e0ee4 The XML output now prints repositories in a proper XML list.
If one repository is found, the following format is used:

<repository>name</repository>

If multiple repositories are found, the following format is used:

<repositories>
	<repository>name</repository>
	<repository>name</repository>
	...
</repositories>
2015-11-03 23:00:17 +01:00
Adam Waldenberg 4d6ecd3123 Avoid hyphen characters ("-") in JSON properties.
Using that character is obviously a bad idea, as it is reserved
for substraction in most programming languages; making output a
hassle to parse.
2015-11-03 22:52:17 +01:00
Adam Waldenberg 9b3a5b674e Some output was missing in the JSON output.
Repository names, gitinspector version and report date were all missing
from the output when JSON was selected.
2015-11-03 22:42:23 +01:00
Adam Waldenberg 87fd5b467f Upgraded the JQuery version in the flot archive to 1.9.1 (#28).
This is the minimal version required by Bootstrap3 (which we will
base the new responsive HTML theme on). This upgrade required changes
to the JavaScript code, as toggle(...) has been deprecated since
version 1.9.x of JQuery.
2015-11-03 04:25:40 +01:00
Adam Waldenberg 4fd918fca4 Fixed some pylint violations. 2015-11-03 00:44:47 +01:00
Adam Waldenberg 949a301698 Fixed a few typo mistakes in the HTML output of the format module. 2015-11-03 00:36:05 +01:00
Adam Waldenberg 211060c20e Updated the flot archive to version 0.8.3. 2015-11-03 00:33:16 +01:00
Adam Waldenberg 9ada057d81 Progress output now includes repository name (#24).
The name of the repository is only printed when multiple repositories
are specified. Otherwise, gitinspector falls back to the previous
behavior
2015-11-02 18:22:56 +01:00
Adam Waldenberg 7dda8d34b5 Made terminal.output_progress(...) aware of the terminal width.
Instead of clearing the row using the "\b" character on each
iteration, the function now passes a carriage return ("\r") instead.
Furthermore, the row is now always completely cleared before
printing the progress.
2015-11-02 18:02:54 +01:00
Adam Waldenberg d5106a7302 Implemented validation and printing of all supplied repositories (#24).
Upon start-up, now directly passes all supplied paths to git in order
to check if all of them point to a valid git repository.

The clone.create() function now returns a list of Repository instances,
containing the name and location of the repository. This is later
used in the gitinspector module to execute and parse the repository.

format.output_header(...) now takes a list of Repository instances.
Thanks to this, each output format can print every supplied repository.
2015-11-02 02:30:28 +01:00
Adam Waldenberg 01bdbfaba1 basedir.get_basedir_git() now takes an optional path argument.
This enables us to change the working directory for the git commands
in the function upon request. This change is in preparation of the
completion of #24.
2015-11-01 17:19:46 +01:00
Adam Waldenberg 7f5b50cd0d Fixed formatting bug in the JSON output of the metrics module.
Sections in the metrics output were not being separated with a comma,
leading to invalid JSON.
2015-11-01 03:45:44 +01:00
Adam Waldenberg 802f18e7e5 The metrics module now supports multiple repositories (See issue #24). 2015-11-01 03:36:40 +01:00
Adam Waldenberg 7acf871ab1 Help text now mentions support for multiple repos (See issue #24). 2015-10-31 05:11:10 +01:00
Adam Waldenberg 98615ccbfc 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.
2015-10-31 05:10:00 +01:00
Adam Waldenberg 46b21db196 The changes module now supports multiple repositories (See issue #24).
Just as before, all other parts of gitinspector currently do not
support fetching statistics from multiple repositories and just
simply fetch data from the last repository specified.
2015-10-31 03:44:00 +01:00
Adam Waldenberg 3bae1be0cb Fixed faulty import in metricsoutput module. 2015-10-31 02:22:25 +01:00
Adam Waldenberg 9287b187f7 Fixed some pylint violations. This also takes care of some bugs. 2015-10-31 00:03:51 +01:00
Adam Waldenberg dd2feedbe4 Fixed invalid indentation at begining of file in gitinspector.py. 2015-10-30 23:54:04 +01:00
Adam Waldenberg e0941fdcf1 The gitinspector.py entry module now considers all repositories.
While they are considered, they are not yet being merged. Statistics
are only calculated for the last repository passed.

This is being done in preparation of completing issue #24.
2015-10-30 23:46:13 +01:00
Adam Waldenberg 109a94e1e7 The basedir module is no longer persistent (doesn't remember state).
This is required in order for it to be callable multiple times for
multiple repositories (Needed for #24).
2015-10-30 23:29:03 +01:00
Adam Waldenberg 258eefa1e7 The clone module can now handle multiple repositories.
This is needed to allow gitinspector to be able to clone multiple
repositories during the same execution (Needed for #24).
2015-10-30 23:16:33 +01:00
Adam Waldenberg d88ff2c5b9 Slight cleanup in gitinspector.py.
Renamed all "local" declarations in main().
2015-10-30 03:41:09 +01:00
Adam Waldenberg c01a59430c Rewrote the config module into a class (GitConfig).
This class takes the initialization variable "global_only" which
will be set in the future whenever multiple repositories are
specified (See issue #24).

This will make sure that the git configuration is only read from the
global settings instead of per-repository.
2015-10-30 01:24:33 +01:00
Adam Waldenberg 5af89f798a Added a note about the supplied Debian packages to README.md. 2015-10-29 03:58:09 +01:00
Adam Waldenberg d30715cc84 The changes module now also outputs the progress when in text mode.
Furthermore, the output also shows the pass currently running. This is
important, as the progress goes from 0 to 100%, twice.
2015-10-29 03:37:08 +01:00
Adam Waldenberg ce91c4176a Moved blame.Blame.output_progress to terminal.output_progress.
This enables us to output progress from multiple modules.
2015-10-29 03:36:53 +01:00
Adam Waldenberg 6606d8b13c Progress from the blame module is now printed regardless of --hard. 2015-10-29 03:36:49 +01:00
Adam Waldenberg 1ed9b5e3cf Re-factored the changes module and made it more independent.
This will make it easier to add support for the merging of statistics from
multiple repositories (as discussed in issue #24).
2015-10-29 03:09:46 +01:00
Adam Waldenberg fa04eb57f3 Bumped the version number to the next development iteration. 2015-10-29 02:25:20 +01:00
Adam Waldenberg d941487280 Imported CHANGES.txt from most recent extra-release branch. 2015-10-29 01:32:15 +01:00
Adam Waldenberg d8dfecb19a Updated typo in RESPONSIBILITIES_INFO_TEXT in all translations.
Also regenerated all .mo files, so these should now all be up-to-date.
2015-10-29 00:11:26 +01:00
Adam Waldenberg 4bd723eea0 Chinese help text would not fit on a 80 character terminal. 2015-10-28 23:59:57 +01:00
Adam Waldenberg 859224f36f Wrong translator specified in Chinese translation. 2015-10-28 23:56:48 +01:00
Bill Wang 5ee5127b15 Fixed typos in RESPONSIBILITIES_INFO_TEXT.
This also requires changes to the templates of the translations.
2015-10-28 23:23:33 +01:00
Bill Wang 75b528a84a Updated Chinese translation (Fixes #11). 2015-10-28 23:23:23 +01:00
Adam Waldenberg 8a0ba3dca1 Updated the header and .mo file of the Italian translation. 2015-10-28 01:01:39 +01:00
Luca Motta 58a983ed27 Updated the Italian translation (Fixes #20). 2015-10-28 00:49:39 +01:00
Adam Waldenberg eec6741cfb Updated header information of french translation and generated .mo file. 2015-10-26 02:19:42 +01:00
Yannick Moy 21c6346868 Updated French translation: help messages. 2015-10-25 21:56:13 +01:00
Adam Waldenberg 78831c1b9b Fixed slight mistake and updated header information in Polish translation.
Also took the opportunity to regenerate the .mo file.
2015-10-24 23:24:28 +02:00
Kamila Chyla 79e390afe8 Updated Polish translation: help messages. 2015-10-24 19:00:54 +02:00
Adam Waldenberg 81e18ff075 Small tweaks to the header of the german translation.
It now correctly references the current pot and version. Consequently, it
should be usable without generating a warning from the current
development iteration of gitinspector.
2015-10-24 17:34:21 +02:00
xxyy b0e7bb5ae9 Update German translation; Fixes #25
This commit updates the German translation. Changes in translation
include updating for changed messages in upstream and some rewording.
Also, this commit includes a different translation for "cyclomatic
complexity" because the previously used one (by inventor's name)
might be less clear than the current, literal translation, which seems
more intuitive, especially in a world where English is dominant in
software dev, so people are more likely to know only the English word.

Some translations have been changed to be more clear and to the point.
Some comments have been added to messages that have been causing issues.
2015-10-24 12:20:49 +02:00
Adam Waldenberg b4eb5484ac README.md now includes a note about JSON being supported. 2015-10-24 03:08:35 +02:00
Adam Waldenberg bbe07f061d Added support for JSON output format (Fixes #50). 2015-10-24 02:44:45 +02:00
Adam Waldenberg cff8dd109b Fixed minor mistake in the filtering XML output.
Nothing that really affects functionality. However, a string was doing
some unnecessary string.format calls.
2015-10-22 00:36:11 +02:00
Adam Waldenberg 124636cb85 Restructured imports slightly. 2015-10-21 05:26:20 +02:00
Adam Waldenberg e9eab37c83 Added a new gitinspector.py entry script. 2015-10-21 05:07:47 +02:00
Marc Harper 59d9e5c7ae Try block unneeded in localization.py import 2015-10-20 09:51:42 -07:00
Marc Harper 4f3e0d3073 Restore relative imports of changes and filtering 2015-10-20 09:48:32 -07:00
Marc Harper 9abb9b3d56 Relative imports to fix packaging issues 2015-10-20 09:40:08 -07:00
Adam Waldenberg f2a4cb92d3 Always append the root gitinspector package to sys.path (Fixes #73).
With this change, gitinspector once again functions in conjunction with
egg installations. Also removed all forceful absolute imports, as these
really aren't needed.
2015-10-15 17:01:57 +02:00
Adam Waldenberg d333f7bdd2 Updated copyright notices of recently updated files. 2015-10-12 03:18:57 +02:00
Adam Waldenberg 1f2f120389 Fixed pylint violations reported by a newer version of pylint. 2015-10-12 03:15:30 +02:00
Adam Waldenberg 34337dec17 Refactored all outputable modules.
This prepares the source code for the changes discussed in issue #24.

Note that this is just a quick restructuring in order to see the resulting
classes and separation. More work will be done to make it more elegant
and with less dependencies between modules.
2015-10-12 03:03:07 +02:00
Adam Waldenberg 6aa41ade9f Bumped the version number to the next development iteration. 2015-10-12 00:48:30 +02:00
Adam Waldenberg fa483b4327 Bumped the version number to 0.4.2. 2015-10-12 00:47:46 +02:00
Adam Waldenberg 5259b76b94 The localization module now warns when finding an out of date translation.
While allowing us to include translations that are not quite up to date
with the current development branch in releases, it also serves to inform
the user that they are using a translation that is broken or incomplete.
2015-10-09 03:02:06 +02:00
Adam Waldenberg 4b92e7a3cc Updated the version string of current up to date translations.
These version strings now reference the current development iteration and
will be updated each time we bump revision.
2015-10-09 02:57:48 +02:00
Agustín Cañas 9368898d6d Updated Spanish translation. Ready for next release 2015-10-08 23:49:31 +02:00
Adam Waldenberg 38df413ebf Global cleanup fixing some pylint violations. 2015-10-05 06:17:10 +02:00
Adam Waldenberg c0cb2d2801 Running setup.py under Python 3 gave an import exception. 2015-10-04 16:05:06 +02:00
Adam Waldenberg 07f17f3d2e Updated Swedish translation. 2015-10-02 04:13:58 +02:00
Adam Waldenberg 47b11addd9 Added an updated messages.pot.
The only thing that has changed since the last .pot file is the help text
(--help). New text has been added to the documentation for the "-x" flag
and the "-f" flag.
2015-10-02 03:51:13 +02:00
Adam Waldenberg aa727a95c3 Documentation update to include the new features on master. 2015-10-01 04:37:20 +02:00
Adam Waldenberg 3e88fcb71a Added the possibility to include all file extensions in the analysis.
This functionality was briefly discussed in issue #61. To make
gitinspector consider all file extensions, it is now possible to supply
a  double asterisk "**" to the list of file extensions.
2015-10-01 03:59:44 +02:00
Adam Waldenberg 243e52b5de The changes module could crash upon no detected file types (Fixes #68).
The sub list flattening happening after all the "changes threads" have
run should be happening before the len(self.commits) check, not after.
2015-09-30 15:10:46 +02:00
Adam Waldenberg e4827ee58e The changes module crashed on empty commit/revision lists (Fixes #68).
Once again introduced with aeb9ad6, this could happen whenever a
repository had no commits or if the history was being filtered with
--since and/or --until and no commits were being detected within that
interval.
2015-09-29 22:09:05 +02:00
Adam Waldenberg 6d89cdf8c8 Added the maintainer of the Spanish translation to README.md. 2015-09-27 12:57:53 +02:00
Agustín Cañas ad36849afb Added Spanish translation (Closes #71). 2015-09-27 12:50:12 +02:00
Adam Waldenberg e3f741b518 Added support for extensionless files (Fixes #60).
This can be enabled by passing a "*" to the list of defined file
types (-f). Originally, the plan was to also implement support for
regular expressions. However, this was skipped, as it complicated how
expressions for the file types flag were being passed on the command line.
2015-09-26 01:52:04 +02:00
Adam Waldenberg 583b5fa753 Python 3 compatibility was broken with commit aeb9ad6 (Fixes #70). 2015-09-25 23:52:53 +02:00
Adam Waldenberg 582ffe7246 Fixed bug introduced with commit aeb9ad6 (Fixes #68).
The interval check was referencing commits instead of self.commits.
2015-09-24 21:40:57 +02:00
Adam Waldenberg a6c05cc619 Added support for comments and metrics in C# code (Fixes #59). 2015-09-24 04:11:38 +02:00
Adam Waldenberg da1553b57e Implemented filtering by commit message (Fixes #57).
When filtering, "git show" is called in order to pull the commit message
from the git log. While slow, it makes certain that the message itself is
pulled correctly, including any escape characters that might be present.
2015-09-24 03:38:35 +02:00
Adam Waldenberg f368c0019a Fixed mistake in the filtering module related to hash filtering.
FILTERING_EMAIL_INFO_TEXT was erroneously being defined twice.
2015-09-22 02:41:16 +02:00
Adam Waldenberg 5a18732112 Added missing file close() in blame module. 2015-09-20 01:43:40 +02:00
Adam Waldenberg b4b48deebd Printed extensions (-l) are now alphabetically sorted. 2015-09-20 01:42:54 +02:00
Adam Waldenberg aeb9ad69f9 Implemented threading in the changes module (Fixes #15).
This change results in a substantial speed up.
2015-09-19 04:32:02 +02:00
Adam Waldenberg 7a9eb69ab0 README.md links to cdn.rawgit.com changed to githubproxy.ejwa.se.
Developed using JavaEE, this is a (currently) private service with
caching for fetching files from GitHub projects. Unlike cdn.rawgit.com,
we don't cache files indefinitely. Instead, we use ETags to control
caching.
2015-09-11 03:51:53 +02:00
Adam Waldenberg f37bdb7c58 Bumped the version number to the next development iteration. 2015-09-08 02:03:11 +02:00
Adam Waldenberg f6fd00f411 Bumped the version number to 0.4.1. 2015-09-08 02:02:21 +02:00
Adam Waldenberg 124ca74650 HTML output was broken when authors with quotes were sent to JavaScript.
Fixed by using json.dumps() to escape the author name before passing it to
the JavaScript output.
2015-09-08 01:35:42 +02:00
Adam Waldenberg 98c375a0ab Fixed bug indirectly introduced with commit 0447da19.
When reversing the git log, we forgot to take the interval into
consideration; breaking the --since and--until flags.
2015-09-07 02:22:58 +02:00
Adam Waldenberg b48c65efb1 All subprocess.Popen calls now use the list variant.
Most (all?) hard coded quotations have been removed in favor of automation
and the pipes or shlex modules.
2015-09-07 01:53:24 +02:00
Adam Waldenberg 25b5507267 Moved test resouces into a resources directory. 2015-09-05 10:27:29 +02:00
Adam Waldenberg c5e861d662 Bumped the version number to the next development iteration. 2015-09-05 10:25:08 +02:00
Adam Waldenberg ade7a5ad9b Bumped the version number to 0.4.0. 2015-09-01 02:58:00 +02:00
Adam Waldenberg b20d91b3cb Added CHANGES document.
This file contains a summary of changes in each public release of
gitinspector.
2015-09-01 02:58:00 +02:00
Adam Waldenberg 15c1816a73 Flag for generating metrics added to the example outputs (README.md). 2015-09-01 02:58:00 +02:00
Adam Waldenberg 8b4ac7d423 Added project badges to the top of README.md (powered by Shields.io). 2015-08-29 01:21:26 +02:00
Adam Waldenberg 52946d4770 README.md links to htmlpreview.github.io changed to cdn.rawgit.com.
This service works much better and does not seem to interfere with the
JQuery scripting like htmlpreview.* does.
2015-08-28 01:41:27 +02:00
Adam Waldenberg bf9e5b700c Added a link to the documentation in README.md 2015-08-28 01:15:00 +02:00
Adam Waldenberg 42b2288c85 Fixed remaining issues in README.md
The logo and example thumbnail are now integrated into the page more nicely.
Also, links to the wiki were not properly defined.
2015-08-25 15:16:19 +02:00
Roman Levin edf1843300 Fix examples table in README.md 2015-08-25 09:40:41 +02:00
Adam Waldenberg 4aa388553c Added an initial README.md to properly introduce the project on GitHub. 2015-08-25 04:29:12 +02:00
Kamila Chyla 96e2b0fdf0 Prevent ZeroDivisionError in get_stability method (Fixes #63).
Signed-off-by: Adam Waldenberg <adam.waldenberg@ejwa.se>
2015-08-25 02:23:46 +02:00
Adam Waldenberg 30a20e6b52 All doc and source code URL's now also point to the new home on GitHub.
Regenerated all the documentation from the AsciiDoc document.
2015-08-24 17:34:14 +02:00
Adam Waldenberg 4803c511df Updated invalid year strings in version information. 2015-08-24 17:23:54 +02:00
Adam Waldenberg 1bc3e026ed Updated README.txt to refer to the new home on GitHub. 2015-08-24 11:03:59 +02:00
Adam Waldenberg e8648a51c2 Added documentation written in AsciiDoc (Fixes issue 45).
Using the AsciiDoc file, documentation was generated in man (troff), PDF
and HTML formats.
2015-02-22 11:22:39 +01:00
Adam Waldenberg 02b6db512b Strip exotic characters from the table-sorter plugin.
This is needed under certain circumstances whenever the terminal locale
can't be determined and can't handle unicode characters.
2015-02-22 00:53:38 +01:00
Adam Waldenberg de602bb70f Added workaround for missing LANG environment variable (Fixes issue 55). 2015-02-16 16:10:12 +01:00
Kamila Chyla 6c45819a62 Updated Polish translation.
Update issue 16
Status: Done
2014-12-30 09:10:32 +01:00
Adam Waldenberg 568a5e5e8b Fixed the terminal column alignment for languages with multi-column chars.
This is for example needed for the Chinese and Korean translations to work
correctly when using terminal output.
2014-12-19 03:02:08 +01:00
Adam Waldenberg b327238496 Fixed a mistake in the Swedish translation.
The translation for the cyclomatic complexity string was using the wrong
format.
2014-12-19 02:59:50 +01:00
Adam Waldenberg 1230fcf80c Changes.get_latest_author_by_email() was broken under Python 3.
This is because of the patch committed with revision 68a6e90228. As
Python 3 stores unicode strings by default, we need to convert them back.

This change will hopefully fix it once and for all.
2014-12-18 04:30:04 +01:00
Adam Waldenberg eed6d0debf localization.get_date() was failing with unicode characters under Python 2.
Fixed by adding a simple decode call.
2014-12-18 04:07:28 +01:00
Adam Waldenberg 8c464fddd4 Some minor tweaks to the German translation. 2014-12-08 11:18:34 +01:00
Philipp Nowak a42726aedb Added German translation.
Update issue 25
Status: Done
2014-12-08 03:41:41 +01:00
Bill Wang e169421fa9 Updated Chinese translation (Fixes issue 11). 2014-12-07 11:27:19 +01:00
Dmitry Dzhus c6928bd675 Recognize Haskell (.hs) comments (Fixes issue 49).
Signed-off-by: Adam Waldenberg <adam.waldenberg@ejwa.se>
2014-12-07 11:03:51 +01:00
Luca Motta cbed2ac13f Updated Italian translation.
Update issue 20
Status: Done
2014-12-03 12:12:39 +01:00
Yannick Moy 2ca997cc9d Added French translation.
Update issue 38
Status: Done
2014-11-30 10:18:18 +01:00
Adam Waldenberg e703856560 The Swedish translation had 2013 instead of 2014 in it's version text. 2014-11-30 10:09:00 +01:00
Adam Waldenberg b22d614849 Recognize comments in .po and .pot files. 2014-11-29 07:01:24 +01:00
Adam Waldenberg 4d6e51a760 Updated the locale template (.pot) and the Swedish translation.
A number of new strings have been added since the last version.
2014-11-29 06:37:34 +01:00
Adam Waldenberg 26e2ce7c07 Added help text (--help) for the new revision filtering. 2014-11-29 06:35:07 +01:00
Adam Waldenberg bc6be1c56f Fixed some pylint violations. 2014-11-27 12:38:58 +01:00
Adam Waldenberg bc9fd8b207 Localized error message in the basedir module. 2014-11-27 12:16:42 +01:00
Adam Waldenberg d315829e9d Added commit filtering by revision (Fixes issue 33).
Specific commits can now be filtered by using;

-x, --exclude="revision:<SHA-1>"

Like with all other filtering in gitinspector, regular expressions are
supported. Likewise, multiple revisions can be specified using a comma or
by simply supplying multiple exclude parameters.

As usual, both the commit module and blame module take this filtering into
account.
2014-11-27 08:39:17 +01:00
Adam Waldenberg 8755fb33dc Doesn't start when using "C" locale (Fixes issue 29). 2014-11-24 09:36:11 +01:00
Christian Kastner 3010359eb2 Add missing html.footer to htmlembedded output (Fixes issue 48).
Signed-off-by: Adam Waldenberg <adam.waldenberg@ejwa.se>
2014-11-23 01:37:18 +01:00
Adam Waldenberg dd50994f2c The metrics module was failing on paths with spaces (Fixes issue 47). 2014-11-21 02:55:29 +01:00
Christian Kastner e3b4857eb6 Gracefully handle non-git-repo path (Fixes issue 44).
Error out gracefully when gitinspector is called with a path that does not
point to a git repository, instead of printing a stack trace.

Signed-off-by: Christian Kastner <debian@kvr.at>
Signed-off-by: Adam Waldenberg <adam.waldenberg@ejwa.se>
2014-11-20 03:41:08 +01:00
Jon Warghed 68a6e90228 Handle escaping in get_latest_author_by_email() (Fixes issue 46).
Signed-off-by: Adam Waldenberg <adam.waldenberg@ejwa.se>
2014-11-19 07:46:07 +01:00
Adam Waldenberg 1d2fd619bd Fixed a special and unique KeyError exception that could occur.
This is because of a mismatch between the changes module and the
blame module. Seems to be triggered by empty rows in source files.
2014-11-06 10:46:23 +01:00
Adam Waldenberg fa5e5411f5 Fixed problem with the new email detection patch. 2014-11-03 11:02:06 +01:00
Yannick Moy 12dce7f365 Recognize comments in Ada and OCaml.
Signed-off-by: Adam Waldenberg <adam.waldenberg@ejwa.se>
2014-11-03 10:34:46 +01:00
Diomidis Spinellis bc5b0abe74 Remove assumption of Python path.
For example, on a FreeBSD system Python can be installed in
/usr/local/bin/python.

Some systems have multiple interpreters installed in a variety of
locations.  The attached patch corrects the problem by using the
env(1) command to run Python.

Signed-off-by: Adam Waldenberg <adam.waldenberg@ejwa.se>
2014-11-03 10:19:04 +01:00
Diomidis Spinellis b20f15e794 More stable author email extraction.
The email search regular expression in the git-blame output can be tripped
by a similar pattern appearing in the source code. The supplied patch
fixes the problem.

Signed-off-by: Adam Waldenberg <adam.waldenberg@ejwa.se>
2014-11-03 10:12:44 +01:00
Chris Barry d6a2f33de5 Use https where possible.
Signed-off-by: Adam Waldenberg <adam.waldenberg@ejwa.se>
2014-11-03 09:56:49 +01:00
Adam Waldenberg 7b61a46d7c The blame chart legend now shows the correct colors (Fixes issue 26).
To also correctly update the legend of the chart, a call to setupGrid()
(in Flot) seems to be required.
2014-07-04 01:37:58 +02:00
Adam Waldenberg 93b18cdd9a The name of the project is now shown in the title of the HTML output.
Moved the gitinspector version number from the title down to the top text
box of the HTML page.
2014-03-17 08:47:49 +01:00
Adam Waldenberg 722ca9b91a The HTML output of the metrics module now shows violation severity.
This is accomplished by color coding the rows of the violations in the
following manner:

minimal violations: light green
minor violations:   green
medium violations:  yellow
bad violations:     light-red
sever violations:   red

Naturally, the zebra coloring of the rows is still being maintained.
2014-02-26 13:53:11 +01:00
Adam Waldenberg 144303dab6 The metrics module cyclomatic complexity density now limited to 3 decimals. 2014-02-26 11:57:43 +01:00
Adam Waldenberg 6052eb5646 Fixed XML output of the metrics module. Also reworked the indentation. 2014-02-26 11:54:23 +01:00
Adam Waldenberg 232e041861 Did some visual improvements to the HTML output of the metrics module.
Each metrics section now has a header with proper coloring. Furthermore,
every other row of the outputted violations are colorized in order to
improve readability.
2014-02-26 11:43:57 +01:00
Adam Waldenberg efe04cc10b Added cyclomatic complexity output to the HTML and XML output formats.
More of the strings in the metrics module are now also localized.
2014-02-25 06:08:26 +01:00
Adam Waldenberg f85c2fe7b4 Removed height adjustment from the HTML body. This isn't needed anymore. 2014-02-24 14:15:37 +01:00
Adam Waldenberg b2528cfda7 Fixed horizontal centering of the header in the HTML output. 2014-02-24 14:14:02 +01:00
Adam Waldenberg fac4b34646 Fixed invalid function call in the responsibilities module. 2014-02-20 21:03:00 +01:00
Adam Waldenberg 9b2aecc3e9 Added support for cyclomatic complexity to the metrics module.
The calculation is rather simple and is not meant to be a reflection of
McCabes's cyclomatic complexity number. Instead, it is an approximation,
implemented in a more general way and supporting many different languages
through the use of regular expression matching.

The initial languages supported in this metric are: Java, JavaScript, C,
C++ and Python.

If anyone needs support for some other language, patches to the metric
module (metrics.py) are welcome.
2014-02-20 20:57:57 +01:00
Adam Waldenberg 786a44ddbc Fixed a newly introduced bug in the resonsibilities module.
The blame module was being called with too few arguments, causing an
exception.
2014-02-20 04:08:05 +01:00
Adam Waldenberg 4e4d0a2ddb The repository name and report date is now included in the generated text. 2014-02-20 04:06:47 +01:00
Adam Waldenberg 12e08daf72 Moved absolute path detection into the basedir module.
The base name of the returned absolute path is also the name of the
repository from which the statistics were gathered. We will use this in
the generated reports.
2014-02-20 03:49:16 +01:00
Adam Waldenberg 0447da1933 The git log is now read in reverse order.
This makes sure that the most recent email of an author is always used when
generating the gravatar. It also makes sure that the mappings between
author<->email are correct (and recent).
2014-02-19 06:36:06 +01:00
Adam Waldenberg e902f7b82d Fixed possible exception occurring during stability value gathering.
For now, we return 100 % whenever an author is not found to have any
entries in the changes log. This can happen whenever an author uses the
same email but with different author names.

The solution would probably be to consider these commits to belong to the
same person and merge the results.

This will require some additional changes to the code and a redesign in
the way that author names and emails are mapped.
2014-02-19 06:33:17 +01:00
Adam Waldenberg 02e0858985 The two generated pie graphs now use the same colors (Fixes issue 26).
The same author should have the same color in the two graphs, something
that is more consistent and logical.
2014-02-14 17:26:26 +01:00
Adam Waldenberg a1e90d0a9d The age value of the blame output now listens to the --weeks flag.
Consequently, the age value now shows the average age of all the rows
blamed to an author in months (or weeks when --weeks/-w is used).
2014-02-14 05:25:14 +01:00
Adam Waldenberg 16154cd0ba Added age value to the blame output (Fixes issue 10).
This completes the "code stability" functionality.

While code stability is a percentage (zero and up) that reflects the
stability of the authors code, the age value is a pseudo-value describing
the average age of all the authors rows. The older the code, the higher
the value.

Code stability can sometimes be above 100%, depending on the way git
calculates insertions and blamed rows.
2014-01-27 03:11:15 +01:00
Adam Waldenberg 98e3f45d65 Added stability value to the blame output. 2014-01-26 02:49:31 +01:00
Adam Waldenberg d99e8cb58d The help text now talks about repositories instead of directories.
This makes more sense now that gitinspector supports remote repositories
in addition to repositories in the local file system.
2014-01-22 00:46:14 +01:00
Adam Waldenberg d8bdbe9f6f Fixed broken header in optval module. 2014-01-22 00:24:22 +01:00
Adam Waldenberg a45df61aa9 Implemented support for remote repositories (Fixes issue 18).
The supported protocols are file://, git://, http://, https:// and ssh://.

Whenever one of the above prefixes are detected in the repository name,
"git clone" is used to clone the repository into a temporary directory.

When "git clone" is called, it's output is redirected to stderr; meaning
that redirection of stdout to a file functions just as before.

If "git clone" fails for some reason, gitinspector will exit; returning
the error code from the "git clone" command.
2014-01-21 12:14:13 +01:00
Adam Waldenberg 974be06f2b Bumped the version number to 0.3.2. 2014-01-15 13:30:15 +01:00
Adam Waldenberg 2b4e6bb111 A newline was missing in the blame output when redirecting stdout. 2014-01-15 13:26:36 +01:00
Kamila Chyla 49c80184e2 Added Polish translation (Fixes issue 16). 2014-01-15 11:17:16 +01:00
Bill Wang f834fc613e Updated Chinese translation (Fixes issue 11). 2014-01-15 11:11:36 +01:00
Adam Waldenberg 1269df639e Part of the help text in the Italian translation was wider than 80 chars. 2014-01-15 10:09:32 +01:00
Adam Waldenberg 37c295186c The --version flag now outputs the correct years (2012-2014).
This also required a small update to all the (up-to-date) language files.
2014-01-15 10:04:46 +01:00
Adam Waldenberg 3d6789f728 Set the zip_safe flag of setup.py to False (Fixes issue 17).
This hopefully fixes the faulty behavior in egg installations.
2014-01-14 07:18:25 +01:00
Luca Motta 949d73a4b4 Added Italian translation (Fixes issue 20). 2014-01-13 17:40:17 +01:00
Adam Waldenberg 8d9d73549c Updated the locale template (.pot) and the Swedish translation.
As soon as the rest of the languages get updated with the new (and
modified) strings from the .pot file, we should be able to tag a new
release.
2014-01-13 13:42:04 +01:00
Adam Waldenberg f044b9ef9d Updated README.txt to cover 2012-2014. 2014-01-13 12:06:40 +01:00
Adam Waldenberg 23bc5fbbea Print a warning when the terminal encoding is not set (Fixes issue 19).
This fix also sets the stdin encoding to UTF-8 whenever it is not set.
The behavior as the same as the one for stdout.
2014-01-08 06:05:10 +01:00
Adam Waldenberg bc00f9731b Fixed some pylint violations in the filtering module. 2014-01-08 04:17:25 +01:00
Adam Waldenberg af7840be81 Added .cc and .hh to the list of default extensions scanned. 2014-01-08 03:51:02 +01:00
Adam Waldenberg a1d83ead9e Added support for comments in .hh files. 2013-12-05 08:47:20 +01:00
Adam Waldenberg abbeed5356 Added support for comments in .cc files. 2013-12-05 08:42:25 +01:00
Adam Waldenberg 37d78ff992 Updated the help command to show information on author/email filtering. 2013-10-30 03:07:45 +01:00
Adam Waldenberg 1a828136b5 Added support for comments in .scala files. 2013-10-28 02:10:09 +01:00
Adam Waldenberg 998ec7456f Added support for comments in .jspx and .xhtml files. 2013-10-25 19:28:31 +02:00
Adam Waldenberg 791e9e39dd Removed unnecessary print in the blame module.
This print should only be enabled in any "interactive format", such as the
terminal (text) output.
2013-09-17 11:56:48 +02:00
Adam Waldenberg e263982806 Progress text of the blame module was being outputted wrong.
Some regression occurred in a previous commit as the progress text never
was purged out of the output.
2013-09-17 11:53:57 +02:00
Adam Waldenberg cf0f40cc4d There was a mistake in the calculation of comments in the blame module.
Whenever filtering of author or email was enabled, problems could arise
when calculating comments percentage. Did some reshuffling of the logic;
resolving it.
2013-08-13 19:26:14 +02:00
Adam Waldenberg c96d3c3ffe The blame module was not behaving quite right when used with "--since".
It was not taking the caret (^) character into consideration when --since
was being specified, something which resulted in an excess of attributed
rows.

With this easy fix, the behavior should now be "correct".
2013-08-13 15:26:42 +02:00
Adam Waldenberg e7d69d78aa Removed some "#!/usr/bin/python" entries that sneaked into a few files.
This entry should only be present in gitinspector.py (our entry point).
2013-08-08 07:10:04 +02:00
Adam Waldenberg 2633e04c3a Completed the support for bare repositories (Fixes issue 14).
With the missing module now gone, only a few minor modifications were
needed to get it working.
2013-08-03 10:34:45 +02:00
Adam Waldenberg bc182c6924 Completely removed the missing module and the --checkout-missing option.
This is flag should not be needed anymore, as gitinspector always uses
a reference point such as HEAD or some reivision when looking into the
repository (never the file structure directly).
2013-08-03 10:30:43 +02:00
Chris Ring 00fdec2928 Initial support for bare repos.
Signed-off-by: Chris Ring <chris@ringthis.com>
Reviewed-by: Adam Waldenberg <adam.waldenberg@ejwa.se>
2013-08-03 09:55:44 +02:00
Adam Waldenberg a4a0e409a2 Filtering rules can now be separated by a comma.
Instead of specifying -x (or --exclude) multiple times, it is possible to
specify multiple filtering rules by separating each rule with a comma (,)
character. This enables the new support for filtering authors and email to
work in conjunction with git-config.

As a side effect, this means that we reserve the comma character for
internal use in gitinspector and that it can't be used in any regular
expression or filtering rule. However, this is not a big problem.

Of course, specifying -x multiple times (like before) is still supported.
2013-08-03 09:41:21 +02:00
Adam Waldenberg 1edae66fee Added filtering of commits from specific authors or emails (Fixes issue 5).
To access this functionality; the -x flag can now be called in the
following ways:

-x file
-x file:<file>
-x author:<author name>
-x email:<email>

Just passing -x file will presume that the filtering rule is intended for
a file (just like the previous behavior).

All the filtering is case sensitive (even filtering by email) in order to
not break any regular expressions used. Case-insensitive matching can
instead be easily achieved with the appropriate regular expression.

To get reversed filtering (excluding everything not matched within -x) a
regular expression with the a syntax such as '^(?!<rule>)' can be used.
2013-07-30 06:32:58 +02:00
65 changed files with 5635 additions and 1486 deletions

2
.gitignore vendored
View File

@ -2,5 +2,7 @@ build
debian
deb_dist
dist
node_modules
*.egg-info
*.pyc
*.tgz

View File

@ -3,7 +3,7 @@ include-ids=yes
comment=yes
[MESSAGES CONTROL]
disable=C0111,R0801,W0232,W0603,W0622,W0702
disable=C0111,R0801,W0232,W0603,W0622,W0702,W0141
[DESIGN]

112
CHANGES.txt Normal file
View File

@ -0,0 +1,112 @@
gitinspector (0.4.4)
Minor release with some minor fixes.
* Better support for additional terminals.
* -f "**" now properly ignores binary files.
gitinspector (0.4.3)
Minor release with updated language locales.
gitinspector (0.4.2)
Minor release with many small, but great improvements.
* The changes module is now threaded and uses all available cores. This
results in a significant speed up.
* Printed extensions are now alphabetically sorted.
* Ability to exclude commits with certain comments from statistics.
* Support for C# code and comments.
* Extensionless files can now be included in statistics.
* Ability to include all file extensions by specifying "**".
* Spanish translation.
gitinspector (0.4.1)
Maintenance release fixing several issues found in the previous version.
gitinspector (0.4.0)
Major update with many fixes and new features.
* Support for remote repositories (git://, http(s)://, ssh://).
* Code stability and age shown in blame output.
* Support for cyclomatic complexity and cyclomatic complexity density
metrics.
* Ability to exclude commit hashes (revisions) from statistics.
* Improved HTML output.
* Support for ADA, OCaml and Haskell comments.
* Improved localization support for languages with multi-column
characters.
* Many (big and small) bug fixes.
* French translation.
* German translation.
gitinspector (0.3.2)
Maintenance release that adds several new features and bug fixes.
* Better handling of terminals that have no encoding configured.
* File extensions .cc and .hh scanned by default.
* Support for bare repositories.
* Support for comments in .xhtml, .jspx, and .scala files.
* The ability to filter out statistics from specific authors or
emails.
* The flag --since now behaves correctly.
* Italian translation.
* Polish translation.
gitinspector (0.3.1)
Minor release which adds support for gravatars.
* A few minor bug fixes.
* Chinese translation.
* Support for gravatars in the HTML and XML outputs.
* Improved responsibilities section in the HTML output.
* Debian package now properly depends on git.
gitinspector (0.3.0)
This is a major release with many new features.
* Support for comments in LaTex (.tex) and PHP files.
* Many bugfixes.
* Localization support (English and Swedish for now).
* Support for setting gitinspector options via git-config.
* Support for regular expressions in conjunction with -x/--exclude.
* New output format; "htmlembedded".
* Support for Python packaging via setuptools.
* Improved HTML output with new JQuery and flot versions.
gitinspector (0.2.2)
This is a maintenance release that fixes several bugs; many related to
character encoding. This version also adds table sorting to the HTML
output.
gitinspector (0.2.1)
Maintenance release that fixes compatibility with Python 3 and
introduces the --since and --until interval flags.
gitinspector (0.2.0)
This release includes several bug fixes and introduces HTML and XML
output formats.
gitinspector (0.1.0)
First public release. While incomplete, this release fulfills the most
important feature requirements that were originally outlined.

56
README.md Normal file
View File

@ -0,0 +1,56 @@
[![Latest release](https://img.shields.io/github/release/ejwa/gitinspector.svg?style=flat-square)](https://github.com/ejwa/gitinspector/releases/latest)
[![License](https://img.shields.io/github/license/ejwa/gitinspector.svg?style=flat-square)](https://github.com/ejwa/gitinspector/blob/master/LICENSE.txt)
<h2>
<img align="left" height="65px"
src="https://raw.githubusercontent.com/ejwa/gitinspector/master/gitinspector/html/gitinspector_piclet.png"/>
&nbsp;About Gitinspector
</h2>
<img align="right" width="30%" src="https://raw.github.com/wiki/ejwa/gitinspector/images/html_example.jpg" />
Gitinspector is a statistical analysis tool for git repositories. The default analysis shows general statistics per author, which can be complemented with a timeline analysis that shows the workload and activity of each author. Under normal operation, it filters the results to only show statistics about a number of given extensions and by default only includes source files in the statistical analysis.
This tool was originally written to help fetch repository statistics from student projects in the course Object-oriented Programming Project (TDA367/DIT211) at Chalmers University of Technology and Gothenburg University.
Today, gitinspector is used as a grading aid by universities worldwide.
A full [Documentation](https://github.com/ejwa/gitinspector/wiki/Documentation) of the usage and available options of gitinspector is available on the wiki. For help on the most common questions, please refer to the [FAQ](https://github.com/ejwa/gitinspector/wiki/FAQ) document.
### Some of the features
* Shows cumulative work by each author in the history.
* Filters results by extension (default: java,c,cc,cpp,h,hh,hpp,py,glsl,rb,js,sql).
* Can display a statistical timeline analysis.
* Scans for all filetypes (by extension) found in the repository.
* Multi-threaded; uses multiple instances of git to speed up analysis when possible.
* Supports HTML, JSON, XML and plain text output (console).
* Can report violations of different code metrics.
### Example outputs
Below are some example outputs for a number of famous open source projects. All the statistics were generated using the *"-HTlrm"* flags.
| Project name | | | | |
|---|---|---|---|---|
| Django | [HTML](http://githubproxy.ejwa.se/wiki/ejwa/gitinspector/examples/django_output.html) | [HTML Embedded](http://githubproxy.ejwa.se/wiki/ejwa/gitinspector/examples/django_output.emb.html) | [Plain Text](http://githubproxy.ejwa.se/wiki/ejwa/gitinspector/examples/django_output.txt) | [XML](http://githubproxy.ejwa.se/wiki/ejwa/gitinspector/examples/django_output.xml) |
| JQuery | [HTML](http://githubproxy.ejwa.se/wiki/ejwa/gitinspector/examples/jquery_output.html) | [HTML Embedded](http://githubproxy.ejwa.se/wiki/ejwa/gitinspector/examples/jquery_output.emb.html) | [Plain Text](http://githubproxy.ejwa.se/wiki/ejwa/gitinspector/examples/jquery_output.txt) | [XML](http://githubproxy.ejwa.se/wiki/ejwa/gitinspector/examples/jquery_output.xml) |
| Pango | [HTML](http://githubproxy.ejwa.se/wiki/ejwa/gitinspector/examples/pango_output.html) | [HTML Embedded](http://githubproxy.ejwa.se/wiki/ejwa/gitinspector/examples/pango_output.emb.html) | [Plain Text](http://githubproxy.ejwa.se/wiki/ejwa/gitinspector/examples/pango_output.txt) | [XML](http://githubproxy.ejwa.se/wiki/ejwa/gitinspector/examples/pango_output.xml) |
### The Team
* Adam Waldenberg, Lead maintainer and Swedish translation
* Agustín Cañas, Spanish translation
* Bart van Andel, npm package maintainer
* Bill Wang, Chinese translation
* Christian Kastner, Debian package maintainer
* Jiwon Kim, Korean translation
* Kamila Chyla, Polish translation
* Luca Motta, Italian translation
* Philipp Nowak, German translation
* Sergei Lomakov, Russian translation
* Yannick Moy, French translation
*We need translations for gitinspector!* If you are a gitinspector user, feel willing to help and have good language skills in any unsupported language we urge you to contact us. We also happily accept code patches. Please refer to [Contributing](https://github.com/ejwa/gitinspector/wiki/Contributing) for more information on how to contribute to the project.
### Packages
The Debian packages offered with releases of gitinspector are unofficial and very simple packages generated with [stdeb](https://github.com/astraw/stdeb). Christian Kastner is maintaining the official Debian packages. You can check the current status on the [Debian Package Tracker](https://tracker.debian.org/pkg/gitinspector). Consequently, there are official packages for many Debian based distributions installable via *apt-get*.
An [npm](https://npmjs.com) package is provided for convenience as well. To install it globally, execute `npm i -g gitinspector`.
### License
gitinspector is licensed under the *GNU GPL v3*. The gitinspector logo is partly based on the git logo; based on the work of Jason Long. The logo is licensed under the *Creative Commons Attribution 3.0 Unported License*.

View File

@ -1,4 +1,4 @@
Copyright © 2012-2013 Ejwa Software. All rights reserved.
Copyright © 2012-2015 Ejwa Software. All rights reserved.
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it under
@ -7,9 +7,9 @@ certain conditions; see the accompanying LICENSE.txt file for further details.
For questions regarding gitinspector you can contact the current maintainer
in charge at gitinspector@ejwa.se.
To run gitinspector; please start it via the gitinspector/gitinspector.py
script. Use the -h or --help flags to get help about available options.
To run gitinspector; please start it via the gitinspector.py script. Use
the -h or --help flags to get help about available options.
It is also possible to set gitinspector options using the "git config"
command. Refer to the project page at http://gitinspector.googlecode.com
command. Refer to the project page at https://github.com/ejwa/gitinspector
for more information.

329
docs/docbook-xsl.css Normal file
View File

@ -0,0 +1,329 @@
/*
CSS stylesheet for XHTML produced by DocBook XSL stylesheets.
*/
body {
font-family: Georgia,serif;
}
code, pre {
font-family: "Courier New", Courier, monospace;
}
span.strong {
font-weight: bold;
}
body blockquote {
margin-top: .75em;
line-height: 1.5;
margin-bottom: .75em;
}
html body {
margin: 1em 5% 1em 5%;
line-height: 1.2;
}
body div {
margin: 0;
}
h1, h2, h3, h4, h5, h6
{
color: #527bbd;
font-family: Arial,Helvetica,sans-serif;
}
div.toc p:first-child,
div.list-of-figures p:first-child,
div.list-of-tables p:first-child,
div.list-of-examples p:first-child,
div.example p.title,
div.sidebar p.title
{
font-weight: bold;
color: #527bbd;
font-family: Arial,Helvetica,sans-serif;
margin-bottom: 0.2em;
}
body h1 {
margin: .0em 0 0 -4%;
line-height: 1.3;
border-bottom: 2px solid silver;
}
body h2 {
margin: 0.5em 0 0 -4%;
line-height: 1.3;
border-bottom: 2px solid silver;
}
body h3 {
margin: .8em 0 0 -3%;
line-height: 1.3;
}
body h4 {
margin: .8em 0 0 -3%;
line-height: 1.3;
}
body h5 {
margin: .8em 0 0 -2%;
line-height: 1.3;
}
body h6 {
margin: .8em 0 0 -1%;
line-height: 1.3;
}
body hr {
border: none; /* Broken on IE6 */
}
div.footnotes hr {
border: 1px solid silver;
}
div.navheader th, div.navheader td, div.navfooter td {
font-family: Arial,Helvetica,sans-serif;
font-size: 0.9em;
font-weight: bold;
color: #527bbd;
}
div.navheader img, div.navfooter img {
border-style: none;
}
div.navheader a, div.navfooter a {
font-weight: normal;
}
div.navfooter hr {
border: 1px solid silver;
}
body td {
line-height: 1.2
}
body th {
line-height: 1.2;
}
ol {
line-height: 1.2;
}
ul, body dir, body menu {
line-height: 1.2;
}
html {
margin: 0;
padding: 0;
}
body h1, body h2, body h3, body h4, body h5, body h6 {
margin-left: 0
}
body pre {
margin: 0.5em 10% 0.5em 1em;
line-height: 1.0;
color: navy;
}
tt.literal, code.literal {
color: navy;
}
.programlisting, .screen {
border: 1px solid silver;
background: #f4f4f4;
margin: 0.5em 10% 0.5em 0;
padding: 0.5em 1em;
}
div.sidebar {
background: #ffffee;
margin: 1.0em 10% 0.5em 0;
padding: 0.5em 1em;
border: 1px solid silver;
}
div.sidebar * { padding: 0; }
div.sidebar div { margin: 0; }
div.sidebar p.title {
margin-top: 0.5em;
margin-bottom: 0.2em;
}
div.bibliomixed {
margin: 0.5em 5% 0.5em 1em;
}
div.glossary dt {
font-weight: bold;
}
div.glossary dd p {
margin-top: 0.2em;
}
dl {
margin: .8em 0;
line-height: 1.2;
}
dt {
margin-top: 0.5em;
}
dt span.term {
font-style: normal;
color: navy;
}
div.variablelist dd p {
margin-top: 0;
}
div.itemizedlist li, div.orderedlist li {
margin-left: -0.8em;
margin-top: 0.5em;
}
ul, ol {
list-style-position: outside;
}
div.sidebar ul, div.sidebar ol {
margin-left: 2.8em;
}
div.itemizedlist p.title,
div.orderedlist p.title,
div.variablelist p.title
{
margin-bottom: -0.8em;
}
div.revhistory table {
border-collapse: collapse;
border: none;
}
div.revhistory th {
border: none;
color: #527bbd;
font-family: Arial,Helvetica,sans-serif;
}
div.revhistory td {
border: 1px solid silver;
}
/* Keep TOC and index lines close together. */
div.toc dl, div.toc dt,
div.list-of-figures dl, div.list-of-figures dt,
div.list-of-tables dl, div.list-of-tables dt,
div.indexdiv dl, div.indexdiv dt
{
line-height: normal;
margin-top: 0;
margin-bottom: 0;
}
/*
Table styling does not work because of overriding attributes in
generated HTML.
*/
div.table table,
div.informaltable table
{
margin-left: 0;
margin-right: 5%;
margin-bottom: 0.8em;
}
div.informaltable table
{
margin-top: 0.4em
}
div.table thead,
div.table tfoot,
div.table tbody,
div.informaltable thead,
div.informaltable tfoot,
div.informaltable tbody
{
/* No effect in IE6. */
border-top: 3px solid #527bbd;
border-bottom: 3px solid #527bbd;
}
div.table thead, div.table tfoot,
div.informaltable thead, div.informaltable tfoot
{
font-weight: bold;
}
div.mediaobject img {
margin-bottom: 0.8em;
}
div.figure p.title,
div.table p.title
{
margin-top: 1em;
margin-bottom: 0.4em;
}
div.calloutlist p
{
margin-top: 0em;
margin-bottom: 0.4em;
}
a img {
border-style: none;
}
@media print {
div.navheader, div.navfooter { display: none; }
}
span.aqua { color: aqua; }
span.black { color: black; }
span.blue { color: blue; }
span.fuchsia { color: fuchsia; }
span.gray { color: gray; }
span.green { color: green; }
span.lime { color: lime; }
span.maroon { color: maroon; }
span.navy { color: navy; }
span.olive { color: olive; }
span.purple { color: purple; }
span.red { color: red; }
span.silver { color: silver; }
span.teal { color: teal; }
span.white { color: white; }
span.yellow { color: yellow; }
span.aqua-background { background: aqua; }
span.black-background { background: black; }
span.blue-background { background: blue; }
span.fuchsia-background { background: fuchsia; }
span.gray-background { background: gray; }
span.green-background { background: green; }
span.lime-background { background: lime; }
span.maroon-background { background: maroon; }
span.navy-background { background: navy; }
span.olive-background { background: olive; }
span.purple-background { background: purple; }
span.red-background { background: red; }
span.silver-background { background: silver; }
span.teal-background { background: teal; }
span.white-background { background: white; }
span.yellow-background { background: yellow; }
span.big { font-size: 2em; }
span.small { font-size: 0.6em; }
span.underline { text-decoration: underline; }
span.overline { text-decoration: overline; }
span.line-through { text-decoration: line-through; }

313
docs/gitinspector.1 Normal file
View File

@ -0,0 +1,313 @@
'\" t
.\" Title: gitinspector
.\" Author: [see the "AUTHOR" section]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 12/14/2015
.\" Manual: The gitinspector Manual
.\" Source: gitinspector 0.4.2
.\" Language: English
.\"
.TH "GITINSPECTOR" "1" "12/14/2015" "gitinspector 0\&.4\&.2" "The gitinspector Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
gitinspector \- statistical analysis tool for git repositories
.SH "SYNOPSIS"
.sp
\fBgitinspector\fR [OPTION]\&... [REPOSITORY]
.SH "DESCRIPTION"
.sp
Analyze and gather statistics about a git repository\&. The defaut analysis shows general statistics per author, which can be complemented with a timeline analysis that shows the workload and activity of each author\&. Under normal operation, gitinspector filters the results to only show statistics about a number of given extensions and by default only includes source files in the statistical analysis\&.
.sp
Several output formats are supported, including plain text, HTML, JSON and XML\&.
.SH "OPTIONS"
.sp
List information about the repository in REPOSITORY\&. If no repository is specified, the current directory is used\&. If multiple repositories are given, information will be fetched from the last repository specified\&.
.sp
Mandatory arguments to long options are mandatory for short options too\&. Boolean arguments can only be given to long options\&.
.PP
\fB\-f, \-\-file\-types\fR=EXTENSIONS
.RS 4
A comma separated list of file extensions to include when computing statistics\&. The default extensions used are: java,c,cc,cpp,h,hh,hpp,py,glsl,rb,js,sql\&. Specifying a single
\fI*\fR
asterisk character includes files with no extension\&. Specifying two consecutive
\fI**\fR
asterisk characters includes all files regardless of extension\&.
.RE
.PP
\fB\-F, \-\-format\fR=FORMAT
.RS 4
Defines in which format output should be generated; the default format is
\fItext\fR
and the available formats are: html,htmlembedded,json,text,xml (see
\fBOUTPUT FORMATS\fR)
.RE
.PP
\fB\-\-grading\fR[=BOOL]
.RS 4
Show statistics and information in a way that is formatted for grading of student projects; this is the same as supplying the options
\fB\-HlmrTw\fR
.RE
.PP
\fB\-H, \-\-hard\fR[=BOOL]
.RS 4
Track rows and look for duplicates harder; this can be quite slow with big repositories
.RE
.PP
\fB\-l, \-\-list\-file\-types\fR[=BOOL]
.RS 4
List all the file extensions available in the current branch of the repository
.RE
.PP
\fB\-L, \-\-localize\-output\fR[=BOOL]
.RS 4
Localize the generated output to the selected system language if a translation is available
.RE
.PP
\fB\-m, \-\-metrics\fR[=BOOL]
.RS 4
Include checks for certain metrics during the analysis of commits
.RE
.PP
\fB\-r \-\-responsibilities\fR[=BOOL]
.RS 4
Show which files the different authors seem most responsible for
.RE
.PP
\fB\-\-since\fR=DATE
.RS 4
Only show statistics for commits more recent than a specific date
.RE
.PP
\fB\-T, \-\-timeline\fR[=BOOL]
.RS 4
Show commit timeline, including author names
.RE
.PP
\fB\-\-until\fR=DATE
.RS 4
Only show statistics for commits older than a specific date
.RE
.PP
\fB\-w, \-\-weeks\fR[=BOOL]
.RS 4
Show all statistical information in weeks instead of in months
.RE
.PP
\fB\-x, \-\-exclude\fR=PATTERN
.RS 4
An exclusion pattern describing the file paths, revisions, author names or author emails that should be excluded from the statistics; can be specified multiple times (see
\fBFILTERING\fR)
.RE
.PP
\fB\-h, \-\-help\fR
.RS 4
Display help and exit
.RE
.PP
\fB\-\-version\fR
.RS 4
Output version information and exit
.RE
.SH "OUTPUT FORMATS"
.sp
There is support for multiple output formats in gitinspector\&. They can be selected using the \fB\-F\fR/\fB\-\-format\fR flags when running the main gitinspector script\&.
.PP
\fBtext (plain text)\fR
.RS 4
Plain text with some very simple ANSI formatting, suitable for console output\&. This is the format chosen by default in gitinspector\&.
.RE
.PP
\fBhtml\fR
.RS 4
HTML with external links\&. The generated HTML page links to some external resources; such as the JavaScript library JQuery\&. It requires an active internet connection to properly function\&. This output format will most likely also link to additional external resources in the future\&.
.RE
.PP
\fBhtmlembedded\fR
.RS 4
HTML with no external links\&. Similar to the HTML output format, but requires no active internet connection\&. As a consequence; the generated pages are bigger (as certain scripts have to be embedded into the generated output)\&.
.RE
.PP
\fBjson\fR
.RS 4
JSON suitable for machine consumption\&. If you want to parse the output generated by gitinspector in a script or application of your own; this format is suitable\&.
.RE
.PP
\fBxml\fR
.RS 4
XML suitable for machine consumption\&. If you want to parse the output generated by gitinspector in a script or application of your own; this format is suitable\&.
.RE
.SH "FILTERING"
.sp
gitinspector offers several different ways of filtering out unwanted information from the generated statistics:
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
.sp -1
.IP \(bu 2.3
.\}
\fBgitinspector \-x myfile\fR, filter out and exclude statistics from all files (or paths) with the string "myfile"
.RE
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
.sp -1
.IP \(bu 2.3
.\}
\fBgitinspector \-x file:myfile\fR, filter out and exclude statistics from all files (or paths) with the string "myfile"
.RE
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
.sp -1
.IP \(bu 2.3
.\}
\fBgitinspector \-x author:John\fR, filter out and exclude statistics from all authors containing the string "John"
.RE
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
.sp -1
.IP \(bu 2.3
.\}
\fBgitinspector \-x email:@gmail\&.com\fR, filter out and exclude statistics from all authors with a gmail account
.RE
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
.sp -1
.IP \(bu 2.3
.\}
\fBgitinspector \-x revision:8755fb33\fR, filter out and exclude statistics from all revisions containing the hash "8755fb33"
.RE
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
.sp -1
.IP \(bu 2.3
.\}
\fBgitinspector \-x message:BUGFIX\fR, filter out and exclude statistics from all revisions containing "BUGFIX" in the commit message\&.
.RE
.sp
The gitinspector command also lets you add multiple filtering rules by simply specifying the \-x options several times or by separating each filtering rule with a comma;
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
.sp -1
.IP \(bu 2.3
.\}
\fBgitinspector \-x author:John \-x email:@gmail\&.com\fR
.RE
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
.sp -1
.IP \(bu 2.3
.\}
\fBgitinspector \-x author:John,email:@gmail\&.com\fR
.RE
.sp
Sometimes, sub\-string matching (as described above) is simply not enough\&. Therefore, gitinspector let\(cqs you specify regular expressions as filtering rules\&. This makes filtering much more flexible:
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
.sp -1
.IP \(bu 2.3
.\}
\fBgitinspector \-x "author:\e^(?!(John Smith))"\fR, only show statistics from author "John Smith"
.RE
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
.sp -1
.IP \(bu 2.3
.\}
\fBgitinspector \-x "author:\e^(?!([A\-C]))"\fR, only show statistics from authors starting with the letters A/B/C
.RE
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
.sp -1
.IP \(bu 2.3
.\}
\fBgitinspector \-x "email:\&.com$"\fR, filter out statistics from all email addresses ending with "\&.com"
.RE
.SH "USING GIT TO CONFIGURE GITINSPECTOR"
.sp
Options in gitinspector can be set using \fBgit config\fR\&. Consequently, it is possible to configure gitinspector behavior globally (in all git repositories) or locally (in a specific git repository)\&. It also means that settings will be permanently stored\&. All the long options that can be given to gitinspector can also be configured via git config (and take the same arguments)\&.
.sp
To configure how gitinspector should behave in all git repositories, execute the following git command:
.sp
\fBgit config \-\-global inspector\&.option setting\fR
.sp
To configure how gitinspector should behave in a specific git repository, execute the following git command (with the current directory standing inside the repository in question):
.sp
\fBgit config inspector\&.option setting\fR
.SH "AUTHOR"
.sp
Originally written by Adam Waldenberg\&.
.SH "REPORTING BUGS"
.sp
Report gitinspector bugs to gitinspector@ejwa\&.se
.sp
The gitinspector project page: https://github\&.com/ejwa/gitinspector
.sp
If you encounter problems, be sure to read the FAQ first: https://github\&.com/ejwa/gitinspector/wiki/FAQ
.sp
There is also an issue tracker at: https://github\&.com/ejwa/gitinspector/issues
.SH "COPYRIGHT"
.sp
Copyright \(co 2012\-2015 Ejwa Software\&. All rights reserved\&. License GPLv3+: GNU GPL version 3 or later http://gnu\&.org/licenses/gpl\&.html\&. This is free software: you are free to change and redistribute it\&. There is NO WARRANTY, to the extent permitted by law\&.
.SH "SEE ALSO"
.sp
\fBgit\fR(1)

105
docs/gitinspector.html Normal file
View File

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>gitinspector</title><link rel="stylesheet" type="text/css" href="docbook-xsl.css" /><meta name="generator" content="DocBook XSL Stylesheets V1.78.1" /></head><body><div xml:lang="en" class="refentry" lang="en"><a id="idp53245840"></a><div class="titlepage"></div><div class="refnamediv"><h2>Name</h2><p>gitinspector — statistical analysis tool for git repositories</p></div><div class="refsynopsisdiv"><a id="_synopsis"></a><h2>Synopsis</h2><p><span class="strong"><strong>gitinspector</strong></span> [OPTION]… [REPOSITORY]</p></div><div class="refsect1"><a id="_description"></a><h2>DESCRIPTION</h2><p>Analyze and gather statistics about a git repository. The defaut analysis shows general statistics per author, which can be complemented with a timeline analysis that shows the workload and activity of each author. Under normal operation, gitinspector filters the results to only show statistics about a number of given extensions and by default only includes source files in the statistical analysis.</p><p>Several output formats are supported, including plain text, HTML, JSON and XML.</p></div><div class="refsect1"><a id="_options"></a><h2>OPTIONS</h2><p>List information about the repository in REPOSITORY. If no repository is specified, the current directory is used. If multiple repositories are given, information will be fetched from the last repository specified.</p><p>Mandatory arguments to long options are mandatory for short options too. Boolean arguments can only be given to long options.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">
<span class="strong"><strong>-f, --file-types</strong></span>=EXTENSIONS
</span></dt><dd>
A comma separated list of file extensions to include when computing statistics. The default extensions used are: java,c,cc,cpp,h,hh,hpp,py,glsl,rb,js,sql. Specifying a single <span class="emphasis"><em>*</em></span> asterisk character includes files with no extension. Specifying two consecutive <span class="emphasis"><em>**</em></span> asterisk characters includes all files regardless of extension.
</dd><dt><span class="term">
<span class="strong"><strong>-F, --format</strong></span>=FORMAT
</span></dt><dd>
Defines in which format output should be generated; the default format is <span class="emphasis"><em>text</em></span> and the available formats are: html,htmlembedded,json,text,xml (see <a class="link" href="#X1" title="OUTPUT FORMATS"><span class="strong"><strong>OUTPUT FORMATS</strong></span></a>)
</dd><dt><span class="term">
<span class="strong"><strong>--grading</strong></span>[=BOOL]
</span></dt><dd>
Show statistics and information in a way that is formatted for grading of student projects; this is the same as supplying the options <span class="strong"><strong>-HlmrTw</strong></span>
</dd><dt><span class="term">
<span class="strong"><strong>-H, --hard</strong></span>[=BOOL]
</span></dt><dd>
Track rows and look for duplicates harder; this can be quite slow with big repositories
</dd><dt><span class="term">
<span class="strong"><strong>-l, --list-file-types</strong></span>[=BOOL]
</span></dt><dd>
List all the file extensions available in the current branch of the repository
</dd><dt><span class="term">
<span class="strong"><strong>-L, --localize-output</strong></span>[=BOOL]
</span></dt><dd>
Localize the generated output to the selected system language if a translation is available
</dd><dt><span class="term">
<span class="strong"><strong>-m, --metrics</strong></span>[=BOOL]
</span></dt><dd>
Include checks for certain metrics during the analysis of commits
</dd><dt><span class="term">
<span class="strong"><strong>-r --responsibilities</strong></span>[=BOOL]
</span></dt><dd>
Show which files the different authors seem most responsible for
</dd><dt><span class="term">
<span class="strong"><strong>--since</strong></span>=DATE
</span></dt><dd>
Only show statistics for commits more recent than a specific date
</dd><dt><span class="term">
<span class="strong"><strong>-T, --timeline</strong></span>[=BOOL]
</span></dt><dd>
Show commit timeline, including author names
</dd><dt><span class="term">
<span class="strong"><strong>--until</strong></span>=DATE
</span></dt><dd>
Only show statistics for commits older than a specific date
</dd><dt><span class="term">
<span class="strong"><strong>-w, --weeks</strong></span>[=BOOL]
</span></dt><dd>
Show all statistical information in weeks instead of in months
</dd><dt><span class="term">
<span class="strong"><strong>-x, --exclude</strong></span>=PATTERN
</span></dt><dd>
An exclusion pattern describing the file paths, revisions, author names or author emails that should be excluded from the statistics; can be specified multiple times (see <a class="link" href="#X2" title="FILTERING"><span class="strong"><strong>FILTERING</strong></span></a>)
</dd><dt><span class="term">
<span class="strong"><strong>-h, --help</strong></span>
</span></dt><dd>
Display help and exit
</dd><dt><span class="term">
<span class="strong"><strong>--version</strong></span>
</span></dt><dd>
Output version information and exit
</dd></dl></div></div><div class="refsect1"><a id="X1"></a><h2>OUTPUT FORMATS</h2><p>There is support for multiple output formats in gitinspector. They can be selected using the <span class="strong"><strong>-F</strong></span>/<span class="strong"><strong>--format</strong></span> flags when running the main gitinspector script.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">
<span class="strong"><strong>text (plain text)</strong></span>
</span></dt><dd>
Plain text with some very simple ANSI formatting, suitable for console output. This is the format chosen by default in gitinspector.
</dd><dt><span class="term">
<span class="strong"><strong>html</strong></span>
</span></dt><dd>
HTML with external links. The generated HTML page links to some external resources; such as the JavaScript library JQuery. It requires an active internet connection to properly function. This output format will most likely also link to additional external resources in the future.
</dd><dt><span class="term">
<span class="strong"><strong>htmlembedded</strong></span>
</span></dt><dd>
HTML with no external links. Similar to the HTML output format, but requires no active internet connection. As a consequence; the generated pages are bigger (as certain scripts have to be embedded into the generated output).
</dd><dt><span class="term">
<span class="strong"><strong>json</strong></span>
</span></dt><dd>
JSON suitable for machine consumption. If you want to parse the output generated by gitinspector in a script or application of your own; this format is suitable.
</dd><dt><span class="term">
<span class="strong"><strong>xml</strong></span>
</span></dt><dd>
XML suitable for machine consumption. If you want to parse the output generated by gitinspector in a script or application of your own; this format is suitable.
</dd></dl></div></div><div class="refsect1"><a id="X2"></a><h2>FILTERING</h2><p>gitinspector offers several different ways of filtering out unwanted information from the generated statistics:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">
<span class="strong"><strong>gitinspector -x myfile</strong></span>, filter out and exclude statistics from all files (or paths) with the string "myfile"
</li><li class="listitem">
<span class="strong"><strong>gitinspector -x file:myfile</strong></span>, filter out and exclude statistics from all files (or paths) with the string "myfile"
</li><li class="listitem">
<span class="strong"><strong>gitinspector -x author:John</strong></span>, filter out and exclude statistics from all authors containing the string "John"
</li><li class="listitem">
<span class="strong"><strong>gitinspector -x email:@gmail.com</strong></span>, filter out and exclude statistics from all authors with a gmail account
</li><li class="listitem">
<span class="strong"><strong>gitinspector -x revision:8755fb33</strong></span>, filter out and exclude statistics from all revisions containing the hash "8755fb33"
</li><li class="listitem">
<span class="strong"><strong>gitinspector -x message:BUGFIX</strong></span>, filter out and exclude statistics from all revisions containing "BUGFIX" in the commit message.
</li></ul></div><p>The gitinspector command also lets you add multiple filtering rules by simply specifying the -x options several times or by separating each filtering rule with a comma;</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">
<span class="strong"><strong>gitinspector -x author:John -x email:@gmail.com</strong></span>
</li><li class="listitem">
<span class="strong"><strong>gitinspector -x author:John,email:@gmail.com</strong></span>
</li></ul></div><p>Sometimes, sub-string matching (as described above) is simply not enough. Therefore, gitinspector lets you specify regular expressions as filtering rules. This makes filtering much more flexible:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">
<span class="strong"><strong>gitinspector -x "author:\^(?!(John Smith))"</strong></span>, only show statistics from author "John Smith"
</li><li class="listitem">
<span class="strong"><strong>gitinspector -x "author:\^(?!([A-C]))"</strong></span>, only show statistics from authors starting with the letters A/B/C
</li><li class="listitem">
<span class="strong"><strong>gitinspector -x "email:.com$"</strong></span>, filter out statistics from all email addresses ending with ".com"
</li></ul></div></div><div class="refsect1"><a id="_using_git_to_configure_gitinspector"></a><h2>USING GIT TO CONFIGURE GITINSPECTOR</h2><p>Options in gitinspector can be set using <span class="strong"><strong>git config</strong></span>. Consequently, it is possible to configure gitinspector behavior globally (in all git repositories) or locally (in a specific git repository). It also means that settings will be permanently stored. All the long options that can be given to gitinspector can also be configured via git config (and take the same arguments).</p><p>To configure how gitinspector should behave in all git repositories, execute the following git command:</p><p><span class="strong"><strong>git config --global inspector.option setting</strong></span></p><p>To configure how gitinspector should behave in a specific git repository, execute the following git command (with the current directory standing inside the repository in question):</p><p><span class="strong"><strong>git config inspector.option setting</strong></span></p></div><div class="refsect1"><a id="_author"></a><h2>AUTHOR</h2><p>Originally written by Adam Waldenberg.</p></div><div class="refsect1"><a id="_reporting_bugs"></a><h2>REPORTING BUGS</h2><p>Report gitinspector bugs to <a class="ulink" href="mailto:gitinspector@ejwa.se" target="_top">gitinspector@ejwa.se</a></p><p>The gitinspector project page: <a class="ulink" href="https://github.com/ejwa/gitinspector" target="_top">https://github.com/ejwa/gitinspector</a></p><p>If you encounter problems, be sure to read the FAQ first: <a class="ulink" href="https://github.com/ejwa/gitinspector/wiki/FAQ" target="_top">https://github.com/ejwa/gitinspector/wiki/FAQ</a></p><p>There is also an issue tracker at: <a class="ulink" href="https://github.com/ejwa/gitinspector/issues" target="_top">https://github.com/ejwa/gitinspector/issues</a></p></div><div class="refsect1"><a id="_copyright"></a><h2>COPYRIGHT</h2><p>Copyright © 2012-2015 Ejwa Software. All rights reserved. License GPLv3+: GNU GPL version 3 or later <a class="ulink" href="http://gnu.org/licenses/gpl.html" target="_top">http://gnu.org/licenses/gpl.html</a>.
This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.</p></div><div class="refsect1"><a id="_see_also"></a><h2>SEE ALSO</h2><p><span class="strong"><strong>git</strong></span>(1)</p></div></div></body></html>

BIN
docs/gitinspector.pdf Normal file

Binary file not shown.

161
docs/gitinspector.txt Normal file
View File

@ -0,0 +1,161 @@
// a2x -v gitinspector.txt; a2x -v -f manpage gitinspector.txt; a2x -v -f xhtml gitinspector.txt
GITINSPECTOR(1)
===============
:doctype: manpage
:man version: 0.4.2
:man source: gitinspector
:man manual: The gitinspector Manual
NAME
----
gitinspector - statistical analysis tool for git repositories
SYNOPSIS
--------
*gitinspector* [OPTION]... [REPOSITORY]
DESCRIPTION
-----------
Analyze and gather statistics about a git repository. The default analysis shows general statistics per author, which can be complemented with a timeline analysis that shows the workload and activity of each author. Under normal operation, gitinspector filters the results to only show statistics about a number of given extensions and by default only includes source files in the statistical analysis.
Several output formats are supported, including plain text, HTML, JSON and XML.
OPTIONS
-------
List information about the repository in REPOSITORY. If no repository is specified, the current directory is used. If multiple repositories are given, information will be fetched from the last repository specified.
Mandatory arguments to long options are mandatory for short options too. Boolean arguments can only be given to long options.
*-f, --file-types*=EXTENSIONS::
A comma separated list of file extensions to include when computing statistics. The default extensions used are: java,c,cc,cpp,h,hh,hpp,py,glsl,rb,js,sql. Specifying a single '\*' asterisk character includes files with no extension. Specifying two consecutive '**' asterisk characters includes all files regardless of extension.
*-F, --format*=FORMAT::
Defines in which format output should be generated; the default format is 'text' and the available formats are: html,htmlembedded,json,text,xml (see <<X1,*OUTPUT FORMATS*>>)
*--grading*[=BOOL]::
Show statistics and information in a way that is formatted for grading of student projects; this is the same as supplying the options *-HlmrTw*
*-H, --hard*[=BOOL]::
Track rows and look for duplicates harder; this can be quite slow with big repositories
*-l, --list-file-types*[=BOOL]::
List all the file extensions available in the current branch of the repository
*-L, --localize-output*[=BOOL]::
Localize the generated output to the selected system language if a translation is available
*-m, --metrics*[=BOOL]::
Include checks for certain metrics during the analysis of commits
*-r --responsibilities*[=BOOL]::
Show which files the different authors seem most responsible for
*--since*=DATE::
Only show statistics for commits more recent than a specific date
*-T, --timeline*[=BOOL]::
Show commit timeline, including author names
*--until*=DATE::
Only show statistics for commits older than a specific date
*-w, --weeks*[=BOOL]::
Show all statistical information in weeks instead of in months
*-x, --exclude*=PATTERN::
An exclusion pattern describing the file paths, revisions, author names or author emails that should be excluded from the statistics; can be specified multiple times (see <<X2,*FILTERING*>>)
*-h, --help*::
Display help and exit
*--version*::
Output version information and exit
[[X1]]
OUTPUT FORMATS
--------------
There is support for multiple output formats in gitinspector. They can be selected using the *-F*/*--format* flags when running the main gitinspector script.
*text (plain text)*::
Plain text with some very simple ANSI formatting, suitable for console output. This is the format chosen by default in gitinspector.
*html*::
HTML with external links. The generated HTML page links to some external resources; such as the JavaScript library JQuery. It requires an active internet connection to properly function. This output format will most likely also link to additional external resources in the future.
*htmlembedded*::
HTML with no external links. Similar to the HTML output format, but requires no active internet connection. As a consequence; the generated pages are bigger (as certain scripts have to be embedded into the generated output).
*json*::
JSON suitable for machine consumption. If you want to parse the output generated by gitinspector in a script or application of your own; this format is suitable.
*xml*::
XML suitable for machine consumption. If you want to parse the output generated by gitinspector in a script or application of your own; this format is suitable.
[[X2]]
FILTERING
---------
gitinspector offers several different ways of filtering out unwanted information from the generated statistics:
* *gitinspector -x myfile*, filter out and exclude statistics from all files (or paths) with the string "myfile"
* *gitinspector -x file:myfile*, filter out and exclude statistics from all files (or paths) with the string "myfile"
* *gitinspector -x author:John*, filter out and exclude statistics from all authors containing the string "John"
* *gitinspector -x email:@gmail.com*, filter out and exclude statistics from all authors with a gmail account
* *gitinspector -x revision:8755fb33*, filter out and exclude statistics from all revisions containing the hash "8755fb33"
* *gitinspector -x message:BUGFIX*, filter out and exclude statistics from all revisions containing "BUGFIX" in the commit message.
The gitinspector command also lets you add multiple filtering rules by simply specifying the -x options several times or by separating each filtering rule with a comma;
* *gitinspector -x author:John -x email:@gmail.com*
* *gitinspector -x author:John,email:@gmail.com*
Sometimes, sub-string matching (as described above) is simply not enough. Therefore, gitinspector let's you specify regular expressions as filtering rules. This makes filtering much more flexible:
* *gitinspector -x "author:\^(?!(John Smith))"*, only show statistics from author "John Smith"
* *gitinspector -x "author:\^(?!([A-C]))"*, only show statistics from authors starting with the letters A/B/C
* *gitinspector -x "email:.com$"*, filter out statistics from all email addresses ending with ".com"
USING GIT TO CONFIGURE GITINSPECTOR
-----------------------------------
Options in gitinspector can be set using *git config*. Consequently, it is possible to configure gitinspector behavior globally (in all git repositories) or locally (in a specific git repository). It also means that settings will be permanently stored. All the long options that can be given to gitinspector can also be configured via git config (and take the same arguments).
To configure how gitinspector should behave in all git repositories, execute the following git command:
*git config --global inspector.option setting*
To configure how gitinspector should behave in a specific git repository, execute the following git command (with the current directory standing inside the repository in question):
*git config inspector.option setting*
AUTHOR
------
Originally written by Adam Waldenberg.
REPORTING BUGS
--------------
Report gitinspector bugs to gitinspector@ejwa.se
The gitinspector project page: <https://github.com/ejwa/gitinspector>
If you encounter problems, be sure to read the FAQ first: <https://github.com/ejwa/gitinspector/wiki/FAQ>
There is also an issue tracker at: <https://github.com/ejwa/gitinspector/issues>
COPYRIGHT
---------
Copyright (C) 2012-2015 Ejwa Software. All rights reserved. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.
SEE ALSO
--------
*git*(1)

29
gitinspector.js Normal file
View File

@ -0,0 +1,29 @@
var PythonShell = require('python-shell');
var options = {
// The main python script is in the same directory as this file
scriptPath: __dirname,
// Get command line arguments, skipping the default node args:
// arg0 == node executable, arg1 == this file
args: process.argv.slice(2)
};
// Set encoding used by stdin etc manually. Without this, gitinspector may fail to run.
process.env.PYTHONIOENCODING = 'utf8';
// Start inspector
var inspector = new PythonShell('gitinspector.py', options);
// Handle stdout
inspector.on('message', function(message) {
console.log(message);
});
// Let the inspector run, catching any error at the end
inspector.end(function (err) {
if (err) {
throw err;
}
});

24
gitinspector.py Executable file
View File

@ -0,0 +1,24 @@
#!/usr/bin/env python
# coding: utf-8
#
# Copyright © 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 gitinspector import gitinspector
if __name__ == "__main__":
gitinspector.main()

View File

@ -1,6 +1,6 @@
# coding: utf-8
#
# Copyright © 2012-2013 Ejwa Software. All rights reserved.
# Copyright © 2012-2015 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -18,10 +18,46 @@
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
import os
import subprocess
import sys
def get_basedir():
if hasattr(sys,'frozen'): # exists when running via py2exe
if hasattr(sys, "frozen"): # exists when running via py2exe
return sys.prefix
else:
return os.path.dirname(os.path.realpath(__file__))
def get_basedir_git(path=None):
previous_directory = None
if path != None:
previous_directory = os.getcwd()
os.chdir(path)
bare_command = subprocess.Popen(["git", "rev-parse", "--is-bare-repository"], bufsize=1,
stdout=subprocess.PIPE, stderr=open(os.devnull, "w"))
isbare = bare_command.stdout.readlines()
bare_command.wait()
if bare_command.returncode != 0:
sys.exit(_("Error processing git repository at \"%s\"." % os.getcwd()))
isbare = (isbare[0].decode("utf-8", "replace").strip() == "true")
absolute_path = None
if isbare:
absolute_path = subprocess.Popen(["git", "rev-parse", "--git-dir"], bufsize=1, stdout=subprocess.PIPE).stdout
else:
absolute_path = subprocess.Popen(["git", "rev-parse", "--show-toplevel"], bufsize=1,
stdout=subprocess.PIPE).stdout
absolute_path = absolute_path.readlines()
if len(absolute_path) == 0:
sys.exit(_("Unable to determine absolute path of git repository."))
if path != None:
os.chdir(previous_directory)
return absolute_path[0].decode("utf-8", "replace").strip()

View File

@ -1,6 +1,6 @@
# coding: utf-8
#
# Copyright © 2012-2013 Ejwa Software. All rights reserved.
# Copyright © 2012-2017 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -19,114 +19,174 @@
from __future__ import print_function
from __future__ import unicode_literals
from localization import N_
from outputable import Outputable
from changes import FileDiff
import comment
import changes
import filtering
import format
import gravatar
import interval
import missing
import datetime
import multiprocessing
import re
import subprocess
import sys
import terminal
import textwrap
import threading
from .localization import N_
from .changes import FileDiff
from . import comment, extensions, filtering, format, interval, terminal
NUM_THREADS = multiprocessing.cpu_count()
class BlameEntry:
class BlameEntry(object):
rows = 0
skew = 0 # Used when calculating average code age.
comments = 0
__thread_lock__ = threading.BoundedSemaphore(NUM_THREADS)
__blame_lock__ = threading.Lock()
AVG_DAYS_PER_MONTH = 30.4167
class BlameThread(threading.Thread):
def __init__(self, blame_string, extension, blames, filename):
def __init__(self, useweeks, changes, blame_command, extension, blames, filename):
__thread_lock__.acquire() # Lock controlling the number of threads running
threading.Thread.__init__(self)
self.blame_string = blame_string
self.useweeks = useweeks
self.changes = changes
self.blame_command = blame_command
self.extension = extension
self.blames = blames
self.filename = filename
self.is_inside_comment = False
def __clear_blamechunk_info__(self):
self.blamechunk_email = None
self.blamechunk_is_last = False
self.blamechunk_is_prior = False
self.blamechunk_revision = None
self.blamechunk_time = None
def __handle_blamechunk_content__(self, content):
author = None
(comments, self.is_inside_comment) = comment.handle_comment_block(self.is_inside_comment, self.extension, content)
if self.blamechunk_is_prior and interval.get_since():
return
try:
author = self.changes.get_latest_author_by_email(self.blamechunk_email)
except KeyError:
return
if not filtering.set_filtered(author, "author") and not \
filtering.set_filtered(self.blamechunk_email, "email") and not \
filtering.set_filtered(self.blamechunk_revision, "revision"):
__blame_lock__.acquire() # Global lock used to protect calls from here...
if self.blames.get((author, self.filename), None) == None:
self.blames[(author, self.filename)] = BlameEntry()
self.blames[(author, self.filename)].comments += comments
self.blames[(author, self.filename)].rows += 1
if (self.blamechunk_time - self.changes.first_commit_date).days > 0:
self.blames[(author, self.filename)].skew += ((self.changes.last_commit_date - self.blamechunk_time).days /
(7.0 if self.useweeks else AVG_DAYS_PER_MONTH))
__blame_lock__.release() # ...to here.
def run(self):
git_blame_r = subprocess.Popen(self.blame_string, shell=True, bufsize=1, stdout=subprocess.PIPE).stdout
is_inside_comment = False
for j in git_blame_r.readlines():
j = j.decode("utf-8", "replace")
if Blame.is_blame_line(j):
author_mail = Blame.get_author_mail(j)
content = Blame.get_content(j)
__blame_lock__.acquire() # Global lock used to protect calls from here...
if self.blames.get((author_mail, self.filename), None) == None:
self.blames[(author_mail, self.filename)] = BlameEntry()
(comments, is_inside_comment) = comment.handle_comment_block(is_inside_comment, self.extension, content)
self.blames[(author_mail, self.filename)].comments += comments
self.blames[(author_mail, self.filename)].rows += 1
__blame_lock__.release() # ...to here.
git_blame_r = subprocess.Popen(self.blame_command, bufsize=1, stdout=subprocess.PIPE).stdout
rows = git_blame_r.readlines()
git_blame_r.close()
self.__clear_blamechunk_info__()
#pylint: disable=W0201
for j in range(0, len(rows)):
row = rows[j].decode("utf-8", "replace").strip()
keyval = row.split(" ", 2)
if self.blamechunk_is_last:
self.__handle_blamechunk_content__(row)
self.__clear_blamechunk_info__()
elif keyval[0] == "boundary":
self.blamechunk_is_prior = True
elif keyval[0] == "author-mail":
self.blamechunk_email = keyval[1].lstrip("<").rstrip(">")
elif keyval[0] == "author-time":
self.blamechunk_time = datetime.date.fromtimestamp(int(keyval[1]))
elif keyval[0] == "filename":
self.blamechunk_is_last = True
elif Blame.is_revision(keyval[0]):
self.blamechunk_revision = keyval[0]
__thread_lock__.release() # Lock controlling the number of threads running
PROGRESS_TEXT = N_("Checking how many rows belong to each author (Progress): {0:.0f}%")
PROGRESS_TEXT = N_("Checking how many rows belong to each author (2 of 2): {0:.0f}%")
class Blame:
def __init__(self, hard):
class Blame(object):
def __init__(self, repo, hard, useweeks, changes):
self.blames = {}
ls_tree_r = subprocess.Popen("git ls-tree --name-only -r " + interval.get_ref(), shell=True, bufsize=1,
stdout=subprocess.PIPE).stdout
lines = ls_tree_r.readlines()
ls_tree_p = subprocess.Popen(["git", "ls-tree", "--name-only", "-r", interval.get_ref()], bufsize=1,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
lines = ls_tree_p.communicate()[0].splitlines()
ls_tree_p.stdout.close()
for i, row in enumerate(lines):
row = row.strip().decode("unicode_escape", "ignore")
row = row.encode("latin-1", "replace")
row = row.decode("utf-8", "replace").strip("\"").strip("'").strip()
if ls_tree_p.returncode == 0:
progress_text = _(PROGRESS_TEXT)
if FileDiff.is_valid_extension(row) and not filtering.set_filtered(FileDiff.get_filename(row)):
if not missing.add(row):
blame_string = "git blame -w {0} ".format("-C -C -M" if hard else "") + \
interval.get_since() + interval.get_ref() + " -- \"" + row + "\""
thread = BlameThread(blame_string, FileDiff.get_extension(row), self.blames, row.strip())
if repo != None:
progress_text = "[%s] " % repo.name + progress_text
for i, row in enumerate(lines):
row = row.strip().decode("unicode_escape", "ignore")
row = row.encode("latin-1", "replace")
row = row.decode("utf-8", "replace").strip("\"").strip("'").strip()
if FileDiff.get_extension(row) in extensions.get_located() and \
FileDiff.is_valid_extension(row) and not filtering.set_filtered(FileDiff.get_filename(row)):
blame_command = filter(None, ["git", "blame", "--line-porcelain", "-w"] + \
(["-C", "-C", "-M"] if hard else []) +
[interval.get_since(), interval.get_ref(), "--", row])
thread = BlameThread(useweeks, changes, blame_command, FileDiff.get_extension(row),
self.blames, row.strip())
thread.daemon = True
thread.start()
if hard:
Blame.output_progress(i, len(lines))
if format.is_interactive_format():
terminal.output_progress(progress_text, i, len(lines))
# 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()
def __iadd__(self, other):
try:
self.blames.update(other.blames)
return self;
except AttributeError:
return other;
@staticmethod
def output_progress(pos, length):
if sys.stdout.isatty() and format.is_interactive_format():
terminal.clear_row()
print("\b" + _(PROGRESS_TEXT).format(100 * pos / length), end="")
sys.stdout.flush()
def is_revision(string):
revision = re.search("([0-9a-f]{40})", string)
if revision == None:
return False
return revision.group(1).strip()
@staticmethod
def is_blame_line(string):
return string.find(" (") != -1
def get_stability(author, blamed_rows, changes):
if author in changes.get_authorinfo_list():
author_insertions = changes.get_authorinfo_list()[author].insertions
return 100 if author_insertions == 0 else 100.0 * blamed_rows / author_insertions
return 100
@staticmethod
def get_author_mail(string):
author_mail = re.search(" \((.*?)\d\d\d\d-\d\d-\d\d", string)
return author_mail.group(1).strip().lstrip("<").rstrip(">")
@staticmethod
def get_content(string):
content = re.search(" \d+\)(.*)", string)
return content.group(1).lstrip()
def get_time(string):
time = re.search(r" \(.*?(\d\d\d\d-\d\d-\d\d)", string)
return time.group(1).strip()
def get_summed_blames(self):
summed_blames = {}
@ -135,112 +195,7 @@ class Blame:
summed_blames[i[0][0]] = BlameEntry()
summed_blames[i[0][0]].rows += i[1].rows
summed_blames[i[0][0]].skew += i[1].skew
summed_blames[i[0][0]].comments += i[1].comments
return summed_blames
__blame__ = None
def get(hard):
global __blame__
if __blame__ == None:
__blame__ = Blame(hard)
return __blame__
BLAME_INFO_TEXT = N_("Below are the number of rows from each author that have survived and are still "
"intact in the current revision")
class BlameOutput(Outputable):
def __init__(self, hard):
self.hard = hard
self.changes = changes.get(hard)
Outputable.__init__(self)
def output_html(self):
get(self.hard)
blame_xml = "<div><div class=\"box\">"
blame_xml += "<p>" + _(BLAME_INFO_TEXT) + ".</p><div><table id=\"blame\" class=\"git\">"
blame_xml += "<thead><tr> <th>{0}</th> <th>{1}</th> <th>{2}</th> </tr></thead>".format(_("Author"),
_("Rows"), _("% in comments"))
blame_xml += "<tbody>"
chart_data = ""
blames = sorted(__blame__.get_summed_blames().items())
total_blames = 0
for i in blames:
total_blames += i[1].rows
for i, entry in enumerate(blames):
work_percentage = str("{0:.2f}".format(100.0 * entry[1].rows / total_blames))
blame_xml += "<tr " + ("class=\"odd\">" if i % 2 == 1 else ">")
if format.get_selected() == "html":
author_email = self.changes.get_author_email(entry[0])
blame_xml += "<td><img src=\"{0}\"/>{1}</td>".format(gravatar.get_url(author_email), entry[0])
else:
blame_xml += "<td>" + entry[0] + "</td>"
blame_xml += "<td>" + str(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 += "</tr>"
chart_data += "{{label: \"{0}\", data: {1}}}".format(entry[0], work_percentage)
if blames[-1] != entry:
chart_data += ", "
blame_xml += "<tfoot><tr> <td colspan=\"3\">&nbsp;</td> </tr></tfoot></tbody></table>"
blame_xml += "<div class=\"chart\" id=\"blame_chart\"></div></div>"
blame_xml += "<script type=\"text/javascript\">"
blame_xml += " $.plot($(\"#blame_chart\"), [{0}], {{".format(chart_data)
blame_xml += " series: {"
blame_xml += " pie: {"
blame_xml += " innerRadius: 0.4,"
blame_xml += " show: true,"
blame_xml += " combine: {"
blame_xml += " threshold: 0.01,"
blame_xml += " label: \"" + _("Minor Authors") + "\""
blame_xml += " }"
blame_xml += " }"
blame_xml += " }, grid: {"
blame_xml += " hoverable: true"
blame_xml += " }"
blame_xml += " });"
blame_xml += "</script></div></div>"
print(blame_xml)
def output_text(self):
print("")
get(self.hard)
if self.hard and sys.stdout.isatty():
terminal.clear_row()
print(textwrap.fill(_(BLAME_INFO_TEXT) + ":", width=terminal.get_size()[0]) + "\n")
terminal.printb(_("Author").ljust(21) + _("Rows").rjust(10) + _("% in comments").rjust(20))
for i in sorted(__blame__.get_summed_blames().items()):
print(i[0].ljust(20)[0:20], end=" ")
print(str(i[1].rows).rjust(10), end=" ")
print("{0:.2f}".format(100.0 * i[1].comments / i[1].rows).rjust(19))
def output_xml(self):
get(self.hard)
message_xml = "\t\t<message>" + _(BLAME_INFO_TEXT) + "</message>\n"
blame_xml = ""
for i in sorted(__blame__.get_summed_blames().items()):
author_email = self.changes.get_author_email(i[0])
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"
rows_xml = "\t\t\t\t<rows>" + str(i[1].rows) + "</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>\n")
blame_xml += "\t\t\t<author>\n" + name_xml + gravatar_xml + rows_xml + percentage_in_comments_xml + "\t\t\t</author>\n"
print("\t<blame>\n" + message_xml + "\t\t<authors>\n" + blame_xml + "\t\t</authors>\n\t</blame>")

View File

@ -1,6 +1,6 @@
# coding: utf-8
#
# Copyright © 2012-2013 Ejwa Software. All rights reserved.
# Copyright © 2012-2017 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -17,21 +17,24 @@
# 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 division
from __future__ import unicode_literals
from localization import N_
from outputable import Outputable
import extensions
import filtering
import format
import gravatar
import interval
import bisect
import datetime
import multiprocessing
import os
import subprocess
import terminal
import textwrap
import threading
from .localization import N_
from . import extensions, filtering, format, interval, terminal
class FileDiff:
CHANGES_PER_THREAD = 200
NUM_THREADS = multiprocessing.cpu_count()
__thread_lock__ = threading.BoundedSemaphore(NUM_THREADS)
__changes_lock__ = threading.Lock()
class FileDiff(object):
def __init__(self, string):
commit_line = string.split("|")
@ -59,20 +62,24 @@ class FileDiff:
extension = FileDiff.get_extension(string)
for i in extensions.get():
if extension == i:
if (extension == "" and i == "*") or extension == i or i == '**':
return True
return False
class Commit:
class Commit(object):
def __init__(self, string):
self.filediffs = []
commit_line = string.split("|")
if commit_line.__len__() == 4:
self.date = commit_line[0]
self.sha = commit_line[1]
self.author = commit_line[2].strip()
self.email = commit_line[3].strip()
if commit_line.__len__() == 5:
self.timestamp = commit_line[0]
self.date = commit_line[1]
self.sha = commit_line[2]
self.author = commit_line[3].strip()
self.email = commit_line[4].strip()
def __lt__(self, other):
return self.timestamp.__lt__(other.timestamp) # only used for sorting; we just consider the timestamp.
def add_filediff(self, filediff):
self.filediffs.append(filediff)
@ -84,33 +91,50 @@ class Commit:
def get_author_and_email(string):
commit_line = string.split("|")
if commit_line.__len__() == 4:
return (commit_line[2].strip(), commit_line[3].strip())
if commit_line.__len__() == 5:
return (commit_line[3].strip(), commit_line[4].strip())
@staticmethod
def is_commit_line(string):
return string.split("|").__len__() == 4
return string.split("|").__len__() == 5
class AuthorInfo:
class AuthorInfo(object):
email = None
insertions = 0
deletions = 0
commits = 0
class Changes:
authors = {}
authors_dateinfo = {}
authors_email = {}
class ChangesThread(threading.Thread):
def __init__(self, hard, changes, first_hash, second_hash, offset):
__thread_lock__.acquire() # Lock controlling the number of threads running
threading.Thread.__init__(self)
self.hard = hard
self.changes = changes
self.first_hash = first_hash
self.second_hash = second_hash
self.offset = offset
@staticmethod
def create(hard, changes, first_hash, second_hash, offset):
thread = ChangesThread(hard, changes, first_hash, second_hash, offset)
thread.daemon = True
thread.start()
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(),
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()
git_log_r.close()
def __init__(self, hard):
self.commits = []
git_log_r = subprocess.Popen("git log --pretty=\"%cd|%H|%aN|%aE\" --stat=100000,8192 --no-merges -w " +
interval.get_since() + interval.get_until() +
"{0} --date=short".format("-C -C -M" if hard else ""),
shell=True, bufsize=1, stdout=subprocess.PIPE).stdout
commit = None
found_valid_extension = False
lines = git_log_r.readlines()
is_filtered = False
commits = []
__changes_lock__.acquire() # Global lock used to protect calls from here...
for i in lines:
j = i.strip().decode("unicode_escape", "ignore")
@ -119,16 +143,26 @@ class Changes:
if Commit.is_commit_line(j):
(author, email) = Commit.get_author_and_email(j)
self.authors_email[author] = email
self.changes.emails_by_author[author] = email
self.changes.authors_by_email[email] = author
if Commit.is_commit_line(j) or i is lines[-1]:
if found_valid_extension:
self.commits.append(commit)
bisect.insort(commits, commit)
found_valid_extension = False
is_filtered = False
commit = Commit(j)
if FileDiff.is_filediff_line(j) and not filtering.set_filtered(FileDiff.get_filename(j)):
if Commit.is_commit_line(j) and \
(filtering.set_filtered(commit.author, "author") or \
filtering.set_filtered(commit.email, "email") or \
filtering.set_filtered(commit.sha, "revision") or \
filtering.set_filtered(commit.sha, "message")):
is_filtered = True
if FileDiff.is_filediff_line(j) and not \
filtering.set_filtered(FileDiff.get_filename(j)) and not is_filtered:
extensions.add_located(FileDiff.get_extension(j))
if FileDiff.is_valid_extension(j):
@ -136,13 +170,91 @@ class Changes:
filediff = FileDiff(j)
commit.add_filediff(filediff)
if interval.has_interval() and len(self.commits) > 0:
interval.set_ref(self.commits[0].sha)
self.changes.commits[self.offset // CHANGES_PER_THREAD] = commits
__changes_lock__.release() # ...to here.
__thread_lock__.release() # Lock controlling the number of threads running
PROGRESS_TEXT = N_("Fetching and calculating primary statistics (1 of 2): {0:.0f}%")
class Changes(object):
authors = {}
authors_dateinfo = {}
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",
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()
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]))
def __iadd__(self, other):
try:
self.authors.update(other.authors)
self.authors_dateinfo.update(other.authors_dateinfo)
self.authors_by_email.update(other.authors_by_email)
self.emails_by_author.update(other.emails_by_author)
for commit in other.commits:
bisect.insort(self.commits, commit)
if not self.commits and not other.commits:
self.commits = []
return self
except AttributeError:
return other
def get_commits(self):
return self.commits
def __modify_authorinfo__(self, authors, key, commit):
@staticmethod
def modify_authorinfo(authors, key, commit):
if authors.get(key, None) == None:
authors[key] = AuthorInfo()
@ -156,149 +268,26 @@ class Changes:
def get_authorinfo_list(self):
if not self.authors:
for i in self.commits:
self.__modify_authorinfo__(self.authors, i.author, i)
Changes.modify_authorinfo(self.authors, i.author, i)
return self.authors
def get_authordateinfo_list(self):
if not self.authors_dateinfo:
for i in self.commits:
self.__modify_authorinfo__(self.authors_dateinfo, (i.date, i.author), i)
Changes.modify_authorinfo(self.authors_dateinfo, (i.date, i.author), i)
return self.authors_dateinfo
def get_author_email(self, name):
return self.authors_email[name]
def get_latest_author_by_email(self, name):
if not hasattr(name, "decode"):
name = str.encode(name)
try:
name = name.decode("unicode_escape", "ignore")
except UnicodeEncodeError:
pass
__changes__ = None
return self.authors_by_email[name]
def get(hard):
global __changes__
if __changes__ == None:
__changes__ = Changes(hard)
return __changes__
HISTORICAL_INFO_TEXT = N_("The following historical commit information, by author, was found in the repository")
NO_COMMITED_FILES_TEXT = N_("No commited files with the specified extensions were found")
class ChangesOutput(Outputable):
def __init__(self, hard):
self.changes = get(hard)
Outputable.__init__(self)
def output_html(self):
authorinfo_list = self.changes.get_authorinfo_list()
total_changes = 0.0
changes_xml = "<div><div class=\"box\">"
chart_data = ""
for i in authorinfo_list:
total_changes += authorinfo_list.get(i).insertions
total_changes += authorinfo_list.get(i).deletions
if authorinfo_list:
changes_xml += "<p>" + _(HISTORICAL_INFO_TEXT) + ".</p><div><table id=\"changes\" class=\"git\">"
changes_xml += "<thead><tr> <th>{0}</th> <th>{1}</th> <th>{2}</th> <th>{3}</th> <th>{4}</th>".format(
_("Author"), _("Commits"), _("Insertions"), _("Deletions"), _("% of changes"))
changes_xml += "</tr></thead><tbody>"
for i, entry in enumerate(sorted(authorinfo_list)):
authorinfo = authorinfo_list.get(entry)
percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
changes_xml += "<tr " + ("class=\"odd\">" if i % 2 == 1 else ">")
if format.get_selected() == "html":
changes_xml += "<td><img src=\"{0}\"/>{1}</td>".format(
gravatar.get_url(self.changes.get_author_email(entry)), entry)
else:
changes_xml += "<td>" + entry + "</td>"
changes_xml += "<td>" + str(authorinfo.commits) + "</td>"
changes_xml += "<td>" + str(authorinfo.insertions) + "</td>"
changes_xml += "<td>" + str(authorinfo.deletions) + "</td>"
changes_xml += "<td>" + "{0:.2f}".format(percentage) + "</td>"
changes_xml += "</tr>"
chart_data += "{{label: \"{0}\", data: {1}}}".format(entry, "{0:.2f}".format(percentage))
if sorted(authorinfo_list)[-1] != entry:
chart_data += ", "
changes_xml += ("<tfoot><tr> <td colspan=\"5\">&nbsp;</td> </tr></tfoot></tbody></table>")
changes_xml += "<div class=\"chart\" id=\"changes_chart\"></div></div>"
changes_xml += "<script type=\"text/javascript\">"
changes_xml += " $.plot($(\"#changes_chart\"), [{0}], {{".format(chart_data)
changes_xml += " series: {"
changes_xml += " pie: {"
changes_xml += " innerRadius: 0.4,"
changes_xml += " show: true,"
changes_xml += " combine: {"
changes_xml += " threshold: 0.01,"
changes_xml += " label: \"" + _("Minor Authors") + "\""
changes_xml += " }"
changes_xml += " }"
changes_xml += " }, grid: {"
changes_xml += " hoverable: true"
changes_xml += " }"
changes_xml += " });"
changes_xml += "</script>"
else:
changes_xml += "<p>" + _(NO_COMMITED_FILES_TEXT) + ".</p>"
changes_xml += "</div></div>"
print(changes_xml)
def output_text(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:
print(textwrap.fill(_(HISTORICAL_INFO_TEXT) + ":", width=terminal.get_size()[0]) + "\n")
terminal.printb(_("Author").ljust(21) + _("Commits").rjust(13) + _("Insertions").rjust(14) +
_("Deletions").rjust(15) + _("% of changes").rjust(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(i.ljust(20)[0:20], 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) + ".")
def output_xml(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_xml = "\t\t<message>" + _(HISTORICAL_INFO_TEXT) + "</message>\n"
changes_xml = ""
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
name_xml = "\t\t\t\t<name>" + i + "</name>\n"
gravatar_xml = "\t\t\t\t<gravatar>" + gravatar.get_url(self.changes.get_author_email(i)) + "</gravatar>\n"
commits_xml = "\t\t\t\t<commits>" + str(authorinfo.commits) + "</commits>\n"
insertions_xml = "\t\t\t\t<insertions>" + str(authorinfo.insertions) + "</insertions>\n"
deletions_xml = "\t\t\t\t<deletions>" + str(authorinfo.deletions) + "</deletions>\n"
percentage_xml = "\t\t\t\t<percentage-of-changes>" + "{0:.2f}".format(percentage) + "</percentage-of-changes>\n"
changes_xml += ("\t\t\t<author>\n" + name_xml + gravatar_xml + commits_xml + insertions_xml +
deletions_xml + percentage_xml + "\t\t\t</author>\n")
print("\t<changes>\n" + message_xml + "\t\t<authors>\n" + changes_xml + "\t\t</authors>\n\t</changes>")
else:
print("\t<changes>\n\t\t<exception>" + _(NO_COMMITED_FILES_TEXT) + "</exception>\n\t</changes>")
def get_latest_email_by_author(self, name):
return self.emails_by_author[name]

58
gitinspector/clone.py Normal file
View File

@ -0,0 +1,58 @@
# coding: utf-8
#
# Copyright © 2014 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 unicode_literals
import os
import shutil
import subprocess
import sys
import tempfile
try:
from urllib.parse import urlparse
except:
from urlparse import urlparse
__cloned_paths__ = []
def create(url):
class Repository(object):
def __init__(self, name, location):
self.name = name
self.location = location
parsed_url = urlparse(url)
if parsed_url.scheme == "file" or parsed_url.scheme == "git" or parsed_url.scheme == "http" or \
parsed_url.scheme == "https" or parsed_url.scheme == "ssh":
path = tempfile.mkdtemp(suffix=".gitinspector")
git_clone = subprocess.Popen(["git", "clone", url, path], bufsize=1, stdout=sys.stderr)
git_clone.wait()
if git_clone.returncode != 0:
sys.exit(git_clone.returncode)
__cloned_paths__.append(path)
return Repository(os.path.basename(parsed_url.path), path)
return Repository(None, os.path.abspath(url))
def delete():
for path in __cloned_paths__:
shutil.rmtree(path, ignore_errors=True)

View File

@ -1,6 +1,6 @@
# coding: utf-8
#
# Copyright © 2012-2013 Ejwa Software. All rights reserved.
# Copyright © 2012-2015 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -19,16 +19,20 @@
from __future__ import unicode_literals
__comment_begining__ = {"java": "/*", "c": "/*", "cpp": "/*", "h": "/*", "hpp": "/*", "html": "<!--", "php": "/*",
"py": "\"\"\"", "glsl": "/*", "rb": "=begin", "js": "/*", "sql": "/*",
"tex": "\\begin{comment}", "xml": "<!--"}
__comment_begining__ = {"java": "/*", "c": "/*", "cc": "/*", "cpp": "/*", "cs": "/*", "h": "/*", "hh": "/*", "hpp": "/*",
"hs": "{-", "html": "<!--", "php": "/*", "py": "\"\"\"", "glsl": "/*", "rb": "=begin", "js": "/*",
"jspx": "<!--", "scala": "/*", "sql": "/*", "tex": "\\begin{comment}", "xhtml": "<!--",
"xml": "<!--", "ml": "(*", "mli": "(*", "go": "/*", "ly": "%{", "ily": "%{"}
__comment_end__ = {"java": "*/", "c": "*/", "cpp": "*/", "h": "*/", "hpp": "*/", "html": "-->", "php": "/*",
"py": "\"\"\"", "glsl": "*/", "rb": "=end", "js": "*/", "sql": "*/",
"tex": "\\end{comment}", "xml": "-->"}
__comment_end__ = {"java": "*/", "c": "*/", "cc": "*/", "cpp": "*/", "cs": "*/", "h": "*/", "hh": "*/", "hpp": "*/", "hs": "-}",
"html": "-->", "php": "*/", "py": "\"\"\"", "glsl": "*/", "rb": "=end", "js": "*/", "jspx": "-->",
"scala": "*/", "sql": "*/", "tex": "\\end{comment}", "xhtml": "-->", "xml": "-->", "ml": "*)", "mli": "*)",
"go": "*/", "ly": "%}", "ily": "%}"}
__comment__ = {"java": "//", "c": "//", "cpp": "//", "h": "//", "hpp": "//", "pl": "#", "php": "//", "py": "#",
"glsl": "//", "rb": "#", "js": "//", "sql": "--", "tex": "%"}
__comment__ = {"java": "//", "c": "//", "cc": "//", "cpp": "//", "cs": "//", "h": "//", "hh": "//", "hpp": "//", "hs": "--",
"pl": "#", "php": "//", "py": "#", "glsl": "//", "rb": "#", "robot": "#", "rs": "//", "rlib": "//", "js": "//",
"scala": "//", "sql": "--", "tex": "%", "ada": "--", "ads": "--", "adb": "--", "pot": "#", "po": "#", "go": "//",
"ly": "%", "ily": "%"}
__comment_markers_must_be_at_begining__ = {"tex": True}

View File

@ -1,6 +1,6 @@
# coding: utf-8
#
# Copyright © 2013 Ejwa Software. All rights reserved.
# Copyright © 2013-2015 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -18,77 +18,76 @@
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
import extensions
import filtering
import format
import interval
import missing
import optval
import os
import subprocess
from . import extensions, filtering, format, interval, optval
def __read_git_config__(repo, variable):
previous_directory = os.getcwd()
os.chdir(repo)
setting = subprocess.Popen("git config inspector." + variable, shell=True, bufsize=1,
stdout=subprocess.PIPE).stdout
os.chdir(previous_directory)
class GitConfig(object):
def __init__(self, run, repo, global_only=False):
self.run = run
self.repo = repo
self.global_only = global_only
try:
setting = setting.readlines()[0]
setting = setting.decode("utf-8", "replace").strip()
except IndexError:
setting = ""
def __read_git_config__(self, variable):
previous_directory = os.getcwd()
os.chdir(self.repo)
setting = subprocess.Popen(filter(None, ["git", "config", "--global" if self.global_only else "",
"inspector." + variable]), bufsize=1, stdout=subprocess.PIPE).stdout
os.chdir(previous_directory)
return setting
try:
setting = setting.readlines()[0]
setting = setting.decode("utf-8", "replace").strip()
except IndexError:
setting = ""
def __read_git_config_bool__(repo, variable):
try:
variable = __read_git_config__(repo, variable)
return optval.get_boolean_argument(False if variable == "" else variable)
except optval.InvalidOptionArgument:
return False
return setting
def __read_git_config_string__(repo, variable):
string = __read_git_config__(repo, variable)
return (True, string) if len(string) > 0 else (False, None)
def __read_git_config_bool__(self, variable):
try:
variable = self.__read_git_config__(variable)
return optval.get_boolean_argument(False if variable == "" else variable)
except optval.InvalidOptionArgument:
return False
def init(run):
missing.set_checkout_missing(__read_git_config_bool__(run.repo, "checkout-missing"))
def __read_git_config_string__(self, variable):
string = self.__read_git_config__(variable)
return (True, string) if len(string) > 0 else (False, None)
var = __read_git_config_string__(run.repo, "file-types")
if var[0]:
extensions.define(var[1])
def read(self):
var = self.__read_git_config_string__("file-types")
if var[0]:
extensions.define(var[1])
var = __read_git_config_string__(run.repo, "exclude")
if var[0]:
filtering.add(var[1])
var = self.__read_git_config_string__("exclude")
if var[0]:
filtering.add(var[1])
var = __read_git_config_string__(run.repo, "format")
if var[0] and not format.select(var[1]):
raise format.InvalidFormatError(_("specified output format not supported."))
var = self.__read_git_config_string__("format")
if var[0] and not format.select(var[1]):
raise format.InvalidFormatError(_("specified output format not supported."))
run.hard = __read_git_config_bool__(run.repo, "hard")
run.list_file_types = __read_git_config_bool__(run.repo, "list-file-types")
run.localize_output = __read_git_config_bool__(run.repo, "localize-output")
run.metrics = __read_git_config_bool__(run.repo, "metrics")
run.responsibilities = __read_git_config_bool__(run.repo, "responsibilities")
run.useweeks = __read_git_config_bool__(run.repo, "weeks")
self.run.hard = self.__read_git_config_bool__("hard")
self.run.list_file_types = self.__read_git_config_bool__("list-file-types")
self.run.localize_output = self.__read_git_config_bool__("localize-output")
self.run.metrics = self.__read_git_config_bool__("metrics")
self.run.responsibilities = self.__read_git_config_bool__("responsibilities")
self.run.useweeks = self.__read_git_config_bool__("weeks")
var = __read_git_config_string__(run.repo, "since")
if var[0]:
interval.set_since(var[1])
var = self.__read_git_config_string__("since")
if var[0]:
interval.set_since(var[1])
var = __read_git_config_string__(run.repo, "until")
if var[0]:
interval.set_until(var[1])
var = self.__read_git_config_string__("until")
if var[0]:
interval.set_until(var[1])
run.timeline = __read_git_config_bool__(run.repo, "timeline")
self.run.timeline = self.__read_git_config_bool__("timeline")
if __read_git_config_bool__(run.repo, "grading"):
run.hard = True
run.list_file_types = True
run.metrics = True
run.responsibilities = True
run.timeline = True
run.useweeks = True
if self.__read_git_config_bool__("grading"):
self.run.hard = True
self.run.list_file_types = True
self.run.metrics = True
self.run.responsibilities = True
self.run.timeline = True
self.run.useweeks = True

View File

@ -1,6 +1,6 @@
# coding: utf-8
#
# Copyright © 2012-2013 Ejwa Software. All rights reserved.
# Copyright © 2012-2015 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -17,14 +17,9 @@
# 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
from localization import N_
from outputable import Outputable
import terminal
import textwrap
DEFAULT_EXTENSIONS = ["java", "c", "cpp", "h", "hpp", "py", "glsl", "rb", "js", "sql"]
DEFAULT_EXTENSIONS = ["java", "c", "cc", "cpp", "h", "hh", "hpp", "py", "glsl", "rb", "js", "sql"]
__extensions__ = DEFAULT_EXTENSIONS
__located_extensions__ = set()
@ -37,51 +32,10 @@ def define(string):
__extensions__ = string.split(",")
def add_located(string):
if len(string) > 0:
if len(string) == 0:
__located_extensions__.add("*")
else:
__located_extensions__.add(string)
EXTENSIONS_INFO_TEXT = N_("The extensions below were found in the repository history")
EXTENSIONS_MARKED_TEXT = N_("(extensions used during statistical analysis are marked)")
class Extensions(Outputable):
def output_html(self):
if __located_extensions__:
extensions_xml = "<div><div class=\"box\">"
extensions_xml += "<p>{0} {1}.</p><p>".format(_(EXTENSIONS_INFO_TEXT), _(EXTENSIONS_MARKED_TEXT))
for i in __located_extensions__:
if i in __extensions__:
extensions_xml += "<strong>" + i + "</strong>"
else:
extensions_xml += i
extensions_xml += " "
extensions_xml += "</p></div></div>"
print(extensions_xml)
def output_text(self):
if __located_extensions__:
print("\n" + textwrap.fill("{0} {1}:".format(_(EXTENSIONS_INFO_TEXT), _(EXTENSIONS_MARKED_TEXT)),
width=terminal.get_size()[0]))
for i in __located_extensions__:
if i in __extensions__:
print("[" + terminal.__bold__ + i + terminal.__normal__ + "]", end=" ")
else:
print (i, end=" ")
print("")
def output_xml(self):
if __located_extensions__:
message_xml = "\t\t<message>" + _(EXTENSIONS_INFO_TEXT) + "</message>\n"
used_extensions_xml = ""
unused_extensions_xml = ""
for i in __located_extensions__:
if i in __extensions__:
used_extensions_xml += "\t\t\t<extension>" + i + "</extension>\n"
else:
unused_extensions_xml += "\t\t\t<extension>" + i + "</extension>\n"
print("\t<extensions>\n" + message_xml + "\t\t<used>\n" + used_extensions_xml + "\t\t</used>\n" +
"\t\t<unused>\n" + unused_extensions_xml + "\t\t</unused>\n" + "\t</extensions>")
def get_located():
return __located_extensions__

View File

@ -1,6 +1,6 @@
# coding: utf-8
#
# Copyright © 2012-2013 Ejwa Software. All rights reserved.
# Copyright © 2012-2015 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -17,16 +17,12 @@
# 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
from localization import N_
from outputable import Outputable
import re
import terminal
import textwrap
import subprocess
__filters__ = []
__filtered_files__ = set()
__filters__ = {"file": [set(), set()], "author": [set(), set()], "email": [set(), set()], "revision": [set(), set()],
"message" : [set(), None]}
class InvalidRegExpError(ValueError):
def __init__(self, msg):
@ -36,57 +32,58 @@ class InvalidRegExpError(ValueError):
def get():
return __filters__
def __add_one__(string):
for i in __filters__:
if (i + ":").lower() == string[0:len(i) + 1].lower():
__filters__[i][0].add(string[len(i) + 1:])
return
__filters__["file"][0].add(string)
def add(string):
__filters__.append(string)
rules = string.split(",")
for rule in rules:
__add_one__(rule)
def clear():
global __filters__
__filters__ = []
for i in __filters__:
__filters__[i][0] = set()
def get_filered():
return __filtered_files__
def get_filered(filter_type="file"):
return __filters__[filter_type][1]
def set_filtered(file_name):
string = file_name.strip()
def has_filtered():
for i in __filters__:
if __filters__[i][1]:
return True
return False
def __find_commit_message__(sha):
git_show_r = subprocess.Popen(filter(None, ["git", "show", "-s", "--pretty=%B", "-w", sha]), bufsize=1,
stdout=subprocess.PIPE).stdout
commit_message = git_show_r.read()
git_show_r.close()
commit_message = commit_message.strip().decode("unicode_escape", "ignore")
commit_message = commit_message.encode("latin-1", "replace")
return commit_message.decode("utf-8", "replace")
def set_filtered(string, filter_type="file"):
string = string.strip()
if len(string) > 0:
for i in __filters__:
for i in __filters__[filter_type][0]:
search_for = string
if filter_type == "message":
search_for = __find_commit_message__(string)
try:
if re.search(i, string) != None:
__filtered_files__.add(string)
if re.search(i, search_for) != None:
if filter_type == "message":
__add_one__("revision:" + string)
else:
__filters__[filter_type][1].add(string)
return True
except:
raise InvalidRegExpError(_("invalid regular expression specified"))
return False
FILTERING_INFO_TEXT = N_("The following files were excluded from the statistics due to the specified exclusion patterns")
class Filtering(Outputable):
def output_html(self):
if __filtered_files__:
filtering_xml = "<div><div class=\"box\">"
filtering_xml += "<p>" + _(FILTERING_INFO_TEXT) + "."+ "</p>"
for i in __filtered_files__:
filtering_xml += "<p>" + i + "</p>"
filtering_xml += "</div></div>"
print(filtering_xml)
def output_text(self):
if __filtered_files__:
print("\n" + textwrap.fill(_(FILTERING_INFO_TEXT) + ":", width=terminal.get_size()[0]))
for i in __filtered_files__:
(width, _unused) = terminal.get_size()
print("...%s" % i[-width+3:] if len(i) > width else i)
def output_xml(self):
if __filtered_files__:
message_xml = "\t\t<message>" + _(FILTERING_INFO_TEXT) + "</message>\n"
filtering_xml = ""
for i in __filtered_files__:
filtering_xml += "\t\t\t<file>" + i + "</file>\n"
print("\t<filering>\n" + message_xml + "\t\t<files>\n" + filtering_xml + "\t\t</files>\n\t</filtering>")

View File

@ -1,6 +1,6 @@
# coding: utf-8
#
# Copyright © 2012-2013 Ejwa Software. All rights reserved.
# Copyright © 2012-2015 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -19,15 +19,17 @@
from __future__ import print_function
from __future__ import unicode_literals
import version
import base64
import basedir
import os
import textwrap
import time
import zipfile
from .localization import N_
from . import basedir, localization, terminal, version
__available_formats__ = ["html", "htmlembedded", "text", "xml"]
__available_formats__ = ["html", "htmlembedded", "json", "text", "xml"]
DEFAULT_FORMAT = __available_formats__[2]
DEFAULT_FORMAT = __available_formats__[3]
__selected_format__ = DEFAULT_FORMAT
@ -51,19 +53,30 @@ def is_interactive_format():
def __output_html_template__(name):
template_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), name)
file_r = open(template_path, "rb")
return file_r.read().decode("utf-8", "replace")
template = file_r.read().decode("utf-8", "replace")
file_r.close()
return template
def __get_zip_file_content__(name, file_name="/html/flot.zip"):
zip_file = zipfile.ZipFile(basedir.get_basedir() + file_name, "r")
content = zip_file.read(name)
zip_file.close()
return content.decode("utf-8", "replace")
def output_header():
INFO_ONE_REPOSITORY = N_("Statistical information for the repository '{0}' was gathered on {1}.")
INFO_MANY_REPOSITORIES = N_("Statistical information for the repositories '{0}' was gathered on {1}.")
def output_header(repos):
repos_string = ", ".join([repo.name for repo in repos])
if __selected_format__ == "html" or __selected_format__ == "htmlembedded":
base = basedir.get_basedir()
html_header = __output_html_template__(base + "/html/html.header")
tablesorter_js = __get_zip_file_content__("jquery.tablesorter.min.js", "/html/jquery.tablesorter.min.js.zip")
tablesorter_js = __get_zip_file_content__("jquery.tablesorter.min.js",
"/html/jquery.tablesorter.min.js.zip").encode("latin-1", "replace")
tablesorter_js = tablesorter_js.decode("utf-8", "ignore")
flot_js = __get_zip_file_content__("jquery.flot.js")
pie_js = __get_zip_file_content__("jquery.flot.pie.js")
resize_js = __get_zip_file_content__("jquery.flot.resize.js")
@ -76,29 +89,66 @@ def output_header():
if __selected_format__ == "htmlembedded":
jquery_js = ">" + __get_zip_file_content__("jquery.js")
else:
jquery_js = " src=\"http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js\">"
jquery_js = " src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js\">"
print(html_header.format(version = version.__version__,
jquery = jquery_js,
jquery_tablesorter = tablesorter_js,
jquery_flot = flot_js,
jquery_flot_pie = pie_js,
jquery_flot_resize = resize_js,
logo = logo.decode("utf-8", "replace"),
logo_text = _("The output has been generated by {0}; the statistical analysis tool"
print(html_header.format(title=_("Repository statistics for '{0}'").format(repos_string),
jquery=jquery_js,
jquery_tablesorter=tablesorter_js,
jquery_flot=flot_js,
jquery_flot_pie=pie_js,
jquery_flot_resize=resize_js,
logo=logo.decode("utf-8", "replace"),
logo_text=_("The output has been generated by {0} {1}. The statistical analysis tool"
" for git repositories.").format(
"<a href=\"http://gitinspector.googlecode.com\">gitinspector</a>"),
show_minor_authors = _("Show minor authors"),
hide_minor_authors = _("Hide minor authors"),
show_minor_rows = _("Show rows with minor work"),
hide_minor_rows = _("Hide rows with minor work")))
"<a href=\"https://github.com/ejwa/gitinspector\">gitinspector</a>",
version.__version__),
repo_text=_(INFO_ONE_REPOSITORY if len(repos) <= 1 else INFO_MANY_REPOSITORIES).format(
repos_string, localization.get_date()),
show_minor_authors=_("Show minor authors"),
hide_minor_authors=_("Hide minor authors"),
show_minor_rows=_("Show rows with minor work"),
hide_minor_rows=_("Hide rows with minor work")))
elif __selected_format__ == "json":
print("{\n\t\"gitinspector\": {")
print("\t\t\"version\": \"" + version.__version__ + "\",")
if len(repos) <= 1:
print("\t\t\"repository\": \"" + repos_string + "\",")
else:
repos_json = "\t\t\"repositories\": [ "
for repo in repos:
repos_json += "\"" + repo.name + "\", "
print(repos_json[:-2] + " ],")
print("\t\t\"report_date\": \"" + time.strftime("%Y/%m/%d") + "\",")
elif __selected_format__ == "xml":
print("<gitinspector>")
print("\t<version>" + version.__version__ + "</version>")
if len(repos) <= 1:
print("\t<repository>" + repos_string + "</repository>")
else:
print("\t<repositories>")
for repo in repos:
print("\t\t<repository>" + repo.name + "</repository>")
print("\t</repositories>")
print("\t<report-date>" + time.strftime("%Y/%m/%d") + "</report-date>")
else:
print(textwrap.fill(_(INFO_ONE_REPOSITORY if len(repos) <= 1 else INFO_MANY_REPOSITORIES).format(
repos_string, localization.get_date()), width=terminal.get_size()[0]))
def output_footer():
if __selected_format__ == "html":
html_footer = __output_html_template__("html/html.footer")
if __selected_format__ == "html" or __selected_format__ == "htmlembedded":
base = basedir.get_basedir()
html_footer = __output_html_template__(base + "/html/html.footer")
print(html_footer)
elif __selected_format__ == "json":
print("\n\t}\n}")
elif __selected_format__ == "xml":
print("</gitinspector>")

198
gitinspector/gitinspector.py Executable file → Normal file
View File

@ -1,7 +1,6 @@
#!/usr/bin/python
# coding: utf-8
#
# Copyright © 2012-2013 Ejwa Software. All rights reserved.
# Copyright © 2012-2015 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -20,80 +19,85 @@
from __future__ import print_function
from __future__ import unicode_literals
import atexit
import getopt
import os
import sys
from .blame import Blame
from .changes import Changes
from .config import GitConfig
from .metrics import MetricsLogic
from . import (basedir, clone, extensions, filtering, format, help, interval,
localization, optval, terminal, version)
from .output import outputable
from .output.blameoutput import BlameOutput
from .output.changesoutput import ChangesOutput
from .output.extensionsoutput import ExtensionsOutput
from .output.filteringoutput import FilteringOutput
from .output.metricsoutput import MetricsOutput
from .output.responsibilitiesoutput import ResponsibilitiesOutput
from .output.timelineoutput import TimelineOutput
import localization
localization.init()
import blame
import changes
import config
import extensions
import filtering
import format
import help
import interval
import getopt
import metrics
import missing
import os
import optval
import outputable
import responsibilities
import subprocess
import sys
import terminal
import timeline
import version
class Runner:
class Runner(object):
def __init__(self):
self.hard = False
self.include_metrics = False
self.list_file_types = False
self.localize_output = False
self.repo = "."
self.responsibilities = False
self.grading = False
self.timeline = False
self.useweeks = False
def output(self):
def process(self, repos):
localization.check_compatibility(version.__version__)
if not self.localize_output:
localization.disable()
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_metrics = MetricsLogic.__new__(MetricsLogic)
os.chdir(self.repo)
absolute_path = subprocess.Popen("git rev-parse --show-toplevel", shell=True, bufsize=1,
stdout=subprocess.PIPE).stdout
absolute_path = absolute_path.readlines()
if len(absolute_path) == 0:
sys.exit(0)
os.chdir(absolute_path[0].decode("utf-8", "replace").strip())
format.output_header()
outputable.output(changes.ChangesOutput(self.hard))
if changes.get(self.hard).get_commits():
outputable.output(blame.BlameOutput(self.hard))
if self.timeline:
outputable.output(timeline.Timeline(changes.get(self.hard), self.useweeks))
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:
outputable.output(metrics.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)
outputable.output(ChangesOutput(summed_changes))
if summed_changes.get_commits():
outputable.output(BlameOutput(summed_changes, summed_blames))
if self.timeline:
outputable.output(TimelineOutput(summed_changes, self.useweeks))
if self.include_metrics:
outputable.output(MetricsOutput(summed_metrics))
if self.responsibilities:
outputable.output(responsibilities.ResponsibilitiesOutput(self.hard))
outputable.output(ResponsibilitiesOutput(summed_changes, summed_blames))
outputable.output(missing.Missing())
outputable.output(filtering.Filtering())
outputable.output(FilteringOutput())
if self.list_file_types:
outputable.output(extensions.Extensions())
outputable.output(ExtensionsOutput())
format.output_footer()
os.chdir(previous_directory)
@ -103,30 +107,44 @@ def __check_python_version__():
python_version = str(sys.version_info[0]) + "." + str(sys.version_info[1])
sys.exit(_("gitinspector requires at least Python 2.6 to run (version {0} was found).").format(python_version))
def __get_validated_git_repos__(repos_relative):
if not repos_relative:
repos_relative = "."
repos = []
#Try to clone the repos or return the same directory and bail out.
for repo in repos_relative:
cloned_repo = clone.create(repo)
if cloned_repo.name == None:
cloned_repo.location = basedir.get_basedir_git(cloned_repo.location)
cloned_repo.name = os.path.basename(cloned_repo.location)
repos.append(cloned_repo)
return repos
def main():
terminal.check_terminal_encoding()
terminal.set_stdin_encoding()
argv = terminal.convert_command_line_to_utf8()
__run__ = Runner()
run = Runner()
repos = []
try:
__opts__, __args__ = optval.gnu_getopt(argv[1:], "cf:F:hHlLmrTwx:", ["checkout-missing:true", "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"])
for arg in __args__:
__run__.repo = arg
opts, args = optval.gnu_getopt(argv[1:], "f:F:hHlLmrTwx:", ["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"])
repos = __get_validated_git_repos__(set(args))
#We need the repo above to be set before we read the git config.
config.init(__run__)
#We need the repos above to be set before we read the git config.
GitConfig(run, repos[-1].location).read()
clear_x_on_next_pass = True
for o, a in __opts__:
if o == "-c":
missing.set_checkout_missing(True)
elif o == "--checkout-missing":
missing.set_checkout_missing(optval.get_boolean_argument(a))
elif o in("-h", "--help"):
for o, a in opts:
if o in("-h", "--help"):
help.output()
sys.exit(0)
elif o in("-f", "--file-types"):
@ -135,25 +153,25 @@ def main():
if not format.select(a):
raise format.InvalidFormatError(_("specified output format not supported."))
elif o == "-H":
__run__.hard = True
run.hard = True
elif o == "--hard":
__run__.hard = optval.get_boolean_argument(a)
run.hard = optval.get_boolean_argument(a)
elif o == "-l":
__run__.list_file_types = True
run.list_file_types = True
elif o == "--list-file-types":
__run__.list_file_types = optval.get_boolean_argument(a)
run.list_file_types = optval.get_boolean_argument(a)
elif o == "-L":
__run__.localize_output = True
run.localize_output = True
elif o == "--localize-output":
__run__.localize_output = optval.get_boolean_argument(a)
run.localize_output = optval.get_boolean_argument(a)
elif o == "-m":
__run__.include_metrics = True
run.include_metrics = True
elif o == "--metrics":
__run__.include_metrics = optval.get_boolean_argument(a)
run.include_metrics = optval.get_boolean_argument(a)
elif o == "-r":
__run__.responsibilities = True
run.responsibilities = True
elif o == "--responsibilities":
__run__.responsibilities = optval.get_boolean_argument(a)
run.responsibilities = optval.get_boolean_argument(a)
elif o == "--since":
interval.set_since(a)
elif o == "--version":
@ -161,23 +179,23 @@ def main():
sys.exit(0)
elif o == "--grading":
grading = optval.get_boolean_argument(a)
__run__.include_metrics = grading
__run__.list_file_types = grading
__run__.responsibilities = grading
__run__.grading = grading
__run__.hard = grading
__run__.timeline = grading
__run__.useweeks = grading
run.include_metrics = grading
run.list_file_types = grading
run.responsibilities = grading
run.grading = grading
run.hard = grading
run.timeline = grading
run.useweeks = grading
elif o == "-T":
__run__.timeline = True
run.timeline = True
elif o == "--timeline":
__run__.timeline = optval.get_boolean_argument(a)
run.timeline = optval.get_boolean_argument(a)
elif o == "--until":
interval.set_until(a)
elif o == "-w":
__run__.useweeks = True
run.useweeks = True
elif o == "--weeks":
__run__.useweeks = optval.get_boolean_argument(a)
run.useweeks = optval.get_boolean_argument(a)
elif o in("-x", "--exclude"):
if clear_x_on_next_pass:
clear_x_on_next_pass = False
@ -185,12 +203,16 @@ def main():
filtering.add(a)
__check_python_version__()
__run__.output()
run.process(repos)
except (filtering.InvalidRegExpError, format.InvalidFormatError, optval.InvalidOptionArgument, getopt.error) as exception:
print(sys.argv[0], "\b:", exception.msg, file=sys.stderr)
print(_("Try `{0} --help' for more information.").format(sys.argv[0]), file=sys.stderr)
sys.exit(2)
@atexit.register
def cleanup():
clone.delete()
if __name__ == "__main__":
main()

View File

@ -1,6 +1,6 @@
# coding: utf-8
#
# Copyright © 2013 Ejwa Software. All rights reserved.
# Copyright © 2013-2014 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -18,21 +18,23 @@
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
import hashlib
try:
from urllib.parse import urlencode
except:
from urllib import urlencode
import format
import hashlib
from . import format
def get_url(email, size=20):
md5hash = hashlib.md5(email.encode("utf-8").lower().strip()).hexdigest()
base_url = "http://www.gravatar.com/avatar/" + md5hash
base_url = "https://www.gravatar.com/avatar/" + md5hash
params = None
if format.get_selected() == "html":
params = {"default": "identicon", "size": size}
elif format.get_selected() == "xml":
elif format.get_selected() == "xml" or format.get_selected() == "json":
params = {"default": "identicon"}
return base_url + "?" + urlencode(params)

View File

@ -1,6 +1,6 @@
# coding: utf-8
#
# Copyright © 2012-2013 Ejwa Software. All rights reserved.
# Copyright © 2012-2015 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -19,22 +19,24 @@
from __future__ import print_function
from __future__ import unicode_literals
from extensions import DEFAULT_EXTENSIONS
from format import __available_formats__
import sys
from .extensions import DEFAULT_EXTENSIONS
from .format import __available_formats__
__doc__ = _("""Usage: {0} [OPTION]... [DIRECTORY]
List information about the repository in DIRECTORY. If no directory is
specified, the current directory is used. If multiple directories are
given, information will be fetched from the last directory specified.
__doc__ = _("""Usage: {0} [OPTION]... [REPOSITORY]...
List information about the repository in REPOSITORY. If no repository is
specified, the current directory is used. If multiple repositories are
given, information will be merged into a unified statistical report.
Mandatory arguments to long options are mandatory for short options too.
Boolean arguments can only be given to long options.
-c, --checkout-missing[=BOOL] try to checkout any missing files
-f, --file-types=EXTENSIONS a comma separated list of file extensions to
include when computing statistics. The
default extensions used are:
{1}
Specifying * includes files with no
extension, while ** includes all files
-F, --format=FORMAT define in which format output should be
generated; the default format is 'text' and
the available formats are:
@ -61,9 +63,11 @@ Boolean arguments can only be given to long options.
specific date
-w, --weeks[=BOOL] show all statistical information in weeks
instead of in months
-x, --exclude=PATTERN an exclusion pattern describing file names
that should be excluded from the statistics;
can be specified multiple times
-x, --exclude=PATTERN an exclusion pattern describing the file
paths, revisions, revisions with certain
commit messages, author names or author
emails that should be excluded from the
statistics; can be specified multiple times
-h, --help display this help and exit
--version output version information and exit

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -2,7 +2,7 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="application/xhtml+xml; charset=utf-8" />
<title>Generated by gitinspector {version}</title>
<title>{title}</title>
<script type="application/javascript"{jquery}</script>
<script type="application/javascript">{jquery_tablesorter}</script>
<script type="application/javascript">{jquery_flot}</script>
@ -26,6 +26,10 @@
}}
}}
// Fix header and set it to the right width.
var remainingHeaderWidth = ($("div.logo").width() - 4) - ($("div.logo img").innerWidth() + 48)
$("div.logo p").css("width", remainingHeaderWidth);
var filterResponsibilities = function() {{
$("table#blame tbody tr td:last-child").filter(function() {{
return parseFloat(this.innerHTML) < MINOR_AUTHOR_PERCENTAGE;
@ -82,15 +86,19 @@
}}).each(function() {{
$(this).addClass("hoverable");
this.innerHTML = "{show_minor_authors} (" + this.hiddenCount + ") &or;";
}}).toggle(function() {{
this.innerHTML = "{hide_minor_authors} (" + this.hiddenCount + ") &and;";
$(this).parent().parent().parent().find("tbody tr").show().each(colorRows);
}}, function() {{
this.innerHTML = "{show_minor_authors} (" + this.hiddenCount + ") &or;";
$(this).parent().parent().parent().find("tbody tr td:last-child").filter(function() {{
return parseFloat(this.innerHTML) < MINOR_AUTHOR_PERCENTAGE;
}}).parent().hide();
$("table.git tbody tr:visible").each(colorRows);
}}).click(function() {{
this.clicked = !this.clicked;
if (this.clicked) {{
this.innerHTML = "{hide_minor_authors} (" + this.hiddenCount + ") &and;";
$(this).parent().parent().parent().find("tbody tr").show().each(colorRows);
}} else {{
this.innerHTML = "{show_minor_authors} (" + this.hiddenCount + ") &or;";
$(this).parent().parent().parent().find("tbody tr td:last-child").filter(function() {{
return parseFloat(this.innerHTML) < MINOR_AUTHOR_PERCENTAGE;
}}).parent().hide();
$("table.git tbody tr:visible").each(colorRows);
}}
}});
filterResponsibilities();
@ -99,12 +107,15 @@
$("div#responsibilities div h3:visible").each(colorRows);
$("div#responsibilities").prepend("<div class=\"button\">{show_minor_authors} (" + hiddenResponsibilitiesCount + ") &or;</div>");
$("div#responsibilities div.button").toggle(function() {{
this.innerHTML = "{hide_minor_authors} (" + hiddenResponsibilitiesCount + ") &and;";
$("div#responsibilities div").show();
}}, function() {{
this.innerHTML = "{show_minor_authors} (" + hiddenResponsibilitiesCount + ") &or;";
filterResponsibilities();
$("div#responsibilities div.button").click(function() {{
this.clicked = !this.clicked;
if (this.clicked) {{
this.innerHTML = "{hide_minor_authors} (" + hiddenResponsibilitiesCount + ") &and;";
$("div#responsibilities div").show();
}} else {{
this.innerHTML = "{show_minor_authors} (" + hiddenResponsibilitiesCount + ") &or;";
filterResponsibilities();
}}
}});
}}
@ -115,13 +126,16 @@
$("div#timeline table.git tbody tr:visible").each(colorRows);
$("div#timeline").prepend("<div class=\"button\">{show_minor_rows} (" + hiddenTimelineCount + ") &or;</div>");
$("div#timeline div.button").toggle(function() {{
this.innerHTML = "{hide_minor_rows} (" + hiddenTimelineCount + ") &and;";
$("div#timeline table.git tbody tr").show().each(colorRows);
}}, function() {{
this.innerHTML = "{show_minor_rows} (" + hiddenTimelineCount + ") &or;";
filterTimeLine();
$("div#timeline table.git tbody tr:visible").each(colorRows);
$("div#timeline div.button").click(function() {{
this.clicked = !this.clicked;
if (this.clicked) {{
this.innerHTML = "{hide_minor_rows} (" + hiddenTimelineCount + ") &and;";
$("div#timeline table.git tbody tr").show().each(colorRows);
}} else {{
this.innerHTML = "{show_minor_rows} (" + hiddenTimelineCount + ") &or;";
filterTimeLine();
$("div#timeline table.git tbody tr:visible").each(colorRows);
}}
}});
}}
@ -142,6 +156,52 @@
this.hoveredElement.removeClass("piehover");
}}
}});
// Make sure the two pie charts use the same colors.
var author_colors = {{}};
$.each(changes_plot.getData(), function(i, v) {{
author_colors[v["label"]] = v["color"];
}});
$.each(blame_plot.getData(), function(i, v) {{
if (author_colors[v["label"]] != undefined) {{
v["color"] = author_colors[v["label"]];
}}
}});
blame_plot.setupGrid();
blame_plot.draw();
// Color in metrics levels.
$("div#metrics div div").each(function() {{
var rgb = $(this).css("background-color").match(/\d+/g);
rgb[0] = parseInt(rgb[0]);
rgb[1] = parseInt(rgb[1]);
rgb[2] = parseInt(rgb[2]);
if ($(this).hasClass("minimal")) {{
rgb[0] -= 10;
rgb[1] += 10;
rgb[2] -= 10;
}} else if ($(this).hasClass("minor")) {{
rgb[1] += 10;
}} else if ($(this).hasClass("medium")) {{
rgb[0] += 10;
rgb[1] += 10;
}} else if ($(this).hasClass("bad")) {{
rgb[0] += 10;
rgb[1] -= 10;
rgb[2] -= 10;
}} else if ($(this).hasClass("severe")) {{
rgb[0] += 20;
rgb[1] -= 20;
rgb[2] -= 20;
}}
$(this).css("background-color", "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")");
}});
}});
</script>
<style type="text/css">
@ -151,7 +211,6 @@
}}
html, body {{
margin: 0;
height: 100%;
font-family: "Arial";
}}
body > div {{
@ -170,10 +229,12 @@
-moz-box-shadow: 1px 1px 3px #666;
}}
div.logo p {{
margin-top: 18px;
width: 60em;
display:inline-block;
vertical-align:middle;
}}
div.logo img {{
float: left;
vertical-align:middle;
padding: 2px 10px 2px 2px;
}}
body > div {{
@ -247,12 +308,15 @@
border: 1px solid #bbb;
cursor: hand;
}}
div#responsibilities div, div#responsibilities div div {{
div#responsibilities div, div#responsibilities div div, div#metrics div, div#metrics div div {{
min-height: 0px;
padding: 0.5em 0.2em;
width: auto;
}}
div#responsibilities div.odd {{
div#metrics div {{
background-color: #eee;
}}
div#responsibilities div.odd, div#metrics div.odd {{
background-color: #dbdbdb;
}}
div#responsibilities p {{
@ -273,13 +337,17 @@
width: 32px;
height: 32px;
}}
h3 {{
h3, h4 {{
border-radius: 8px 8px 8px 8px;
-moz-border-radius: 8px 8px 8px 8px;
background-color: #ddcece;
margin-bottom: 0.2em;
margin-top: 0.6em;
}}
h4 {{
margin-top: 0.2em;
padding: 0.5em;
}}
div.button, div#responsibilities div.button {{
border-radius: 8px 8px 8px 8px;
-moz-border-radius: 8px 8px 8px 8px;
@ -294,6 +362,6 @@
</head>
<body>
<div><div class="box logo">
<img src="data:image/png;base64,{logo}" />
<p>{logo_text}<p>
<a href="https://github.com/ejwa/gitinspector"><img src="data:image/png;base64,{logo}" /></a>
<p>{repo_text}<br>{logo_text}</p>
</div></div>

View File

@ -1,7 +1,6 @@
#!/usr/bin/python
# coding: utf-8
#
# Copyright © 2012-2013 Ejwa Software. All rights reserved.
# Copyright © 2012-2015 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -20,6 +19,11 @@
from __future__ import unicode_literals
try:
from shlex import quote
except ImportError:
from pipes import quote
__since__ = ""
__until__ = ""
@ -34,14 +38,14 @@ def get_since():
def set_since(since):
global __since__
__since__ = "--since=\"" + since + "\" "
__since__ = "--since=" + quote(since)
def get_until():
return __until__
def set_until(until):
global __until__
__until__ = "--until=\"" + until + "\" "
__until__ = "--until=" + quote(until)
def get_ref():
return __ref__

View File

@ -1,7 +1,6 @@
#!/usr/bin/python
# coding: utf-8
#
# Copyright © 2013 Ejwa Software. All rights reserved.
# Copyright © 2013-2015 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -18,12 +17,17 @@
# 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 basedir
import gettext
import locale
import os
import re
import sys
import time
from . import basedir
__enabled__ = False
__installed__ = False
__translation__ = None
@ -32,6 +36,7 @@ def N_(message):
return message
def init():
global __enabled__
global __installed__
global __translation__
@ -46,22 +51,56 @@ def init():
#Fix for non-POSIX-compliant systems (Windows et al.).
if os.getenv('LANG') is None:
lang = locale.getdefaultlocale()
os.environ['LANG'] = lang[0]
filename = basedir.get_basedir() + "/translations/messages_%s.mo" % lang[0][0:2]
if lang[0]:
os.environ['LANG'] = lang[0]
try:
__translation__ = gettext.GNUTranslations(open(filename, "rb"))
except IOError:
if lang[0] is not None:
filename = basedir.get_basedir() + "/translations/messages_%s.mo" % lang[0][0:2]
try:
__translation__ = gettext.GNUTranslations(open(filename, "rb"))
except IOError:
__translation__ = gettext.NullTranslations()
else:
print("WARNING: Localization disabled because the system language could not be determined.", file=sys.stderr)
__translation__ = gettext.NullTranslations()
__enabled__ = True
__installed__ = True
__translation__.install(True)
def check_compatibility(version):
if isinstance(__translation__, gettext.GNUTranslations):
header_pattern = re.compile("^([^:\n]+): *(.*?) *$", re.MULTILINE)
header_entries = dict(header_pattern.findall(_("")))
if header_entries["Project-Id-Version"] != "gitinspector {0}".format(version):
print("WARNING: The translation for your system locale is not up to date with the current gitinspector "
"version. The current maintainer of this locale is {0}.".format(header_entries["Last-Translator"]),
file=sys.stderr)
def get_date():
if __enabled__ and isinstance(__translation__, gettext.GNUTranslations):
date = time.strftime("%x")
if hasattr(date, 'decode'):
date = date.decode("utf-8", "replace")
return date
else:
return time.strftime("%Y/%m/%d")
def enable():
if __installed__ and type(__translation__) is not gettext.GNUTranslations:
if isinstance(__translation__, gettext.GNUTranslations):
__translation__.install(True)
global __enabled__
__enabled__ = True
def disable():
global __enabled__
__enabled__ = False
if __installed__:
gettext.NullTranslations().install(True)

View File

@ -1,6 +1,6 @@
# coding: utf-8
#
# Copyright © 2012-2013 Ejwa Software. All rights reserved.
# Copyright © 2012-2017 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -17,94 +17,109 @@
# 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
from localization import N_
from outputable import Outputable
from changes import FileDiff
import comment
import filtering
import interval
import missing
import re
import subprocess
from .changes import FileDiff
from . import comment, filtering, interval
__metric_eloc__ = {"java": 500, "c": 500, "cpp": 500, "h": 300, "hpp": 300, "php": 500, "py": 500, "glsl": 1000,
__metric_eloc__ = {"java": 500, "c": 500, "cpp": 500, "cs": 500, "h": 300, "hpp": 300, "php": 500, "py": 500, "glsl": 1000,
"rb": 500, "js": 500, "sql": 1000, "xml": 1000}
class MetricsLogic:
__metric_cc_tokens__ = [[["java", "js", "c", "cc", "cpp"], ["else", r"for\s+\(.*\)", r"if\s+\(.*\)", r"case\s+\w+:",
"default:", r"while\s+\(.*\)"],
["assert", "break", "continue", "return"]],
[["cs"], ["else", r"for\s+\(.*\)", r"foreach\s+\(.*\)", r"goto\s+\w+:", r"if\s+\(.*\)", r"case\s+\w+:",
"default:", r"while\s+\(.*\)"],
["assert", "break", "continue", "return"]],
[["py"], [r"^\s+elif .*:$", r"^\s+else:$", r"^\s+for .*:", r"^\s+if .*:$", r"^\s+while .*:$"],
[r"^\s+assert", "break", "continue", "return"]]]
METRIC_CYCLOMATIC_COMPLEXITY_THRESHOLD = 50
METRIC_CYCLOMATIC_COMPLEXITY_DENSITY_THRESHOLD = 0.75
class MetricsLogic(object):
def __init__(self):
self.eloc = {}
ls_tree_r = subprocess.Popen("git ls-tree --name-only -r " + interval.get_ref(), shell=True, bufsize=1,
stdout=subprocess.PIPE).stdout
self.cyclomatic_complexity = {}
self.cyclomatic_complexity_density = {}
for i in ls_tree_r.readlines():
i = i.strip().decode("unicode_escape", "ignore")
i = i.encode("latin-1", "replace")
i = i.decode("utf-8", "replace").strip("\"").strip("'").strip()
ls_tree_p = subprocess.Popen(["git", "ls-tree", "--name-only", "-r", interval.get_ref()], bufsize=1,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
lines = ls_tree_p.communicate()[0].splitlines()
ls_tree_p.stdout.close()
if ls_tree_p.returncode == 0:
for i in lines:
i = i.strip().decode("unicode_escape", "ignore")
i = i.encode("latin-1", "replace")
i = i.decode("utf-8", "replace").strip("\"").strip("'").strip()
if FileDiff.is_valid_extension(i) and not filtering.set_filtered(FileDiff.get_filename(i)):
file_r = subprocess.Popen(["git", "show", interval.get_ref() + ":{0}".format(i.strip())],
bufsize=1, stdout=subprocess.PIPE).stdout.readlines()
if FileDiff.is_valid_extension(i) and not filtering.set_filtered(FileDiff.get_filename(i)):
if not missing.add(i):
file_r = open(i.strip(), "rb")
extension = FileDiff.get_extension(i)
lines = MetricsLogic.get_eloc(file_r, extension)
cycc = MetricsLogic.get_cyclomatic_complexity(file_r, extension)
if __metric_eloc__.get(extension, None) != None and __metric_eloc__[extension] < lines:
self.eloc[i.strip()] = lines
if METRIC_CYCLOMATIC_COMPLEXITY_THRESHOLD < cycc:
self.cyclomatic_complexity[i.strip()] = cycc
if lines > 0 and METRIC_CYCLOMATIC_COMPLEXITY_DENSITY_THRESHOLD < cycc / float(lines):
self.cyclomatic_complexity_density[i.strip()] = cycc / float(lines)
def __iadd__(self, other):
try:
self.eloc.update(other.eloc)
self.cyclomatic_complexity.update(other.cyclomatic_complexity)
self.cyclomatic_complexity_density.update(other.cyclomatic_complexity_density)
return self
except AttributeError:
return other;
@staticmethod
def get_cyclomatic_complexity(file_r, extension):
is_inside_comment = False
cc_counter = 0
entry_tokens = None
exit_tokens = None
for i in __metric_cc_tokens__:
if extension in i[0]:
entry_tokens = i[1]
exit_tokens = i[2]
if entry_tokens or exit_tokens:
for i in file_r:
i = i.decode("utf-8", "replace")
(_, is_inside_comment) = comment.handle_comment_block(is_inside_comment, extension, i)
if not is_inside_comment and not comment.is_comment(extension, i):
for j in entry_tokens:
if re.search(j, i, re.DOTALL):
cc_counter += 2
for j in exit_tokens:
if re.search(j, i, re.DOTALL):
cc_counter += 1
return cc_counter
return -1
@staticmethod
def get_eloc(file_r, extension):
is_inside_comment = False
eloc_counter = 0
for j in file_r.readlines():
j = j.decode("utf-8", "replace")
(_, is_inside_comment) = comment.handle_comment_block(is_inside_comment, extension, j)
for i in file_r:
i = i.decode("utf-8", "replace")
(_, is_inside_comment) = comment.handle_comment_block(is_inside_comment, extension, i)
if not is_inside_comment and not comment.is_comment(extension, j):
if not is_inside_comment and not comment.is_comment(extension, i):
eloc_counter += 1
return eloc_counter
ELOC_INFO_TEXT = N_("The following files are suspiciously big (in order of severity)")
METRICS_MISSING_INFO_TEXT = N_("No metrics violations were found in the repository")
class Metrics(Outputable):
def output_text(self):
metrics_logic = MetricsLogic()
if not metrics_logic.eloc:
print("\n" + _(METRICS_MISSING_INFO_TEXT) + ".")
else:
print("\n" + _(ELOC_INFO_TEXT) + ":")
for i in sorted(set([(j, i) for (i, j) in metrics_logic.eloc.items()]), reverse = True):
print(i[1] + " (" + str(i[0]) + " eloc)")
def output_html(self):
metrics_logic = MetricsLogic()
metrics_xml = "<div><div class=\"box\">"
if not metrics_logic.eloc:
metrics_xml += "<p>" + _(METRICS_MISSING_INFO_TEXT) + ".</p>"
else:
metrics_xml += "<p>" + _(ELOC_INFO_TEXT) + ".</p>"
for i in sorted(set([(j, i) for (i, j) in metrics_logic.eloc.items()]), reverse = True):
metrics_xml += "<p>" + i[1] + " (" + str(i[0]) + " eloc)</p>"
metrics_xml += "</div></div>"
print(metrics_xml)
def output_xml(self):
metrics_logic = MetricsLogic()
if not metrics_logic.eloc:
print("\t<metrics>\n\t\t<message>" + _(METRICS_MISSING_INFO_TEXT) + "</message>\n\t</metrics>")
else:
eloc_xml = ""
for i in sorted(set([(j, i) for (i, j) in metrics_logic.eloc.items()]), reverse = True):
eloc_xml += "\t\t\t\t\t<violation>\n"
eloc_xml += "\t\t\t\t\t\t<file-name>" + i[1] + "</file-name>\n"
eloc_xml += "\t\t\t\t\t\t<lines-of-code>" + str(i[0]) + "</lines-of-code>\n"
eloc_xml += "\t\t\t\t\t</violation>\n"
print("\t\t<metrics>\n\t\t\t<eloc>\n\t\t\t\t<message>" + _(ELOC_INFO_TEXT) +
"</message>\n\t\t\t\t<violations>\n" + eloc_xml + "\t\t\t\t</violations>\n\t\t\t</eloc>\n\t\t</metrics>")

View File

@ -1,78 +0,0 @@
# coding: utf-8
#
# Copyright © 2012-2013 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
from localization import N_
from outputable import Outputable
import interval
import os
import subprocess
import terminal
import textwrap
__checkout_missing__ = False
__missing_files__ = set()
def add(file_name):
if not interval.has_interval() and not os.path.exists(file_name):
if __checkout_missing__:
subprocess.call("git checkout \"" + file_name.strip() + "\"", shell=True)
else:
__missing_files__.add(file_name)
return True
return False
def set_checkout_missing(checkout):
global __checkout_missing__
__checkout_missing__ = checkout
MISSING_INFO_TEXT = N_("The following files were missing in the repository and were therefore not "
"completely included in the statistical analysis. To include them, you can "
"either checkout manually using git or use the -c option in gitinspector")
class Missing(Outputable):
def output_html(self):
if __missing_files__:
missing_xml = "<div><div class=\"box\">"
missing_xml += "<p>" + _(MISSING_INFO_TEXT) + ".</p>"
for missing in __missing_files__:
missing_xml += "<p class=\"error\">" + missing + "</p>"
missing_xml += "</div></div>"
print(missing_xml)
def output_text(self):
if __missing_files__:
print("\n" + textwrap.fill(_(MISSING_INFO_TEXT) + ":", width=terminal.get_size()[0]))
for missing in __missing_files__:
(width, _unused) = terminal.get_size()
print("...%s" % missing[-width+3:] if len(missing) > width else missing)
def output_xml(self):
if __missing_files__:
message_xml = "\t\t<message>" + _(MISSING_INFO_TEXT) + "</message>\n"
missing_xml = ""
for missing in __missing_files__:
missing_xml += "\t\t\t<file>" + missing + "</file>\n"
print("\t<missing>\n" + message_xml + "\t\t<files>\n" + missing_xml + "\t\t</files>\n\t</missing>")

View File

@ -15,6 +15,7 @@
# 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 unicode_literals
import getopt

View File

@ -0,0 +1,20 @@
# coding: utf-8
#
# Copyright © 2013 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/>.
# This file was intentionally left blank.

View File

@ -0,0 +1,154 @@
# 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 sys
import textwrap
from ..localization import N_
from .. import format, gravatar, terminal
from ..blame import Blame
from .outputable import Outputable
BLAME_INFO_TEXT = N_("Below are the number of rows from each author that have survived and are still "
"intact in the current revision")
class BlameOutput(Outputable):
def __init__(self, changes, blame):
if format.is_interactive_format():
print("")
self.changes = changes
self.blame = blame
Outputable.__init__(self)
def output_html(self):
blame_xml = "<div><div class=\"box\">"
blame_xml += "<p>" + _(BLAME_INFO_TEXT) + ".</p><div><table id=\"blame\" class=\"git\">"
blame_xml += "<thead><tr> <th>{0}</th> <th>{1}</th> <th>{2}</th> <th>{3}</th> <th>{4}</th> </tr></thead>".format(
_("Author"), _("Rows"), _("Stability"), _("Age"), _("% in comments"))
blame_xml += "<tbody>"
chart_data = ""
blames = sorted(self.blame.get_summed_blames().items())
total_blames = 0
for i in blames:
total_blames += i[1].rows
for i, entry in enumerate(blames):
work_percentage = str("{0:.2f}".format(100.0 * entry[1].rows / total_blames))
blame_xml += "<tr " + ("class=\"odd\">" if i % 2 == 1 else ">")
if format.get_selected() == "html":
author_email = self.changes.get_latest_email_by_author(entry[0])
blame_xml += "<td><img src=\"{0}\"/>{1}</td>".format(gravatar.get_url(author_email), entry[0])
else:
blame_xml += "<td>" + entry[0] + "</td>"
blame_xml += "<td>" + str(entry[1].rows) + "</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:.2f}".format(100.0 * entry[1].comments / entry[1].rows) + "</td>"
blame_xml += "<td style=\"display: none\">" + work_percentage + "</td>"
blame_xml += "</tr>"
chart_data += "{{label: {0}, data: {1}}}".format(json.dumps(entry[0]), work_percentage)
if blames[-1] != entry:
chart_data += ", "
blame_xml += "<tfoot><tr> <td colspan=\"5\">&nbsp;</td> </tr></tfoot></tbody></table>"
blame_xml += "<div class=\"chart\" id=\"blame_chart\"></div></div>"
blame_xml += "<script type=\"text/javascript\">"
blame_xml += " blame_plot = $.plot($(\"#blame_chart\"), [{0}], {{".format(chart_data)
blame_xml += " series: {"
blame_xml += " pie: {"
blame_xml += " innerRadius: 0.4,"
blame_xml += " show: true,"
blame_xml += " combine: {"
blame_xml += " threshold: 0.01,"
blame_xml += " label: \"" + _("Minor Authors") + "\""
blame_xml += " }"
blame_xml += " }"
blame_xml += " }, grid: {"
blame_xml += " hoverable: true"
blame_xml += " }"
blame_xml += " });"
blame_xml += "</script></div></div>"
print(blame_xml)
def output_json(self):
message_json = "\t\t\t\"message\": \"" + _(BLAME_INFO_TEXT) + "\",\n"
blame_json = ""
for i in sorted(self.blame.get_summed_blames().items()):
author_email = self.changes.get_latest_email_by_author(i[0])
name_json = "\t\t\t\t\"name\": \"" + i[0] + "\",\n"
email_json = "\t\t\t\t\"email\": \"" + author_email + "\",\n"
gravatar_json = "\t\t\t\t\"gravatar\": \"" + gravatar.get_url(author_email) + "\",\n"
rows_json = "\t\t\t\t\"rows\": " + str(i[1].rows) + ",\n"
stability_json = ("\t\t\t\t\"stability\": " + "{0:.1f}".format(Blame.get_stability(i[0], i[1].rows,
self.changes)) + ",\n")
age_json = ("\t\t\t\t\"age\": " + "{0:.1f}".format(float(i[1].skew) / i[1].rows) + ",\n")
percentage_in_comments_json = ("\t\t\t\t\"percentage_in_comments\": " +
"{0:.2f}".format(100.0 * i[1].comments / i[1].rows) + "\n")
blame_json += ("{\n" + name_json + email_json + gravatar_json + rows_json + stability_json + age_json +
percentage_in_comments_json + "\t\t\t},")
else:
blame_json = blame_json[:-1]
print(",\n\t\t\"blame\": {\n" + message_json + "\t\t\t\"authors\": [\n\t\t\t" + blame_json + "]\n\t\t}", end="")
def output_text(self):
if sys.stdout.isatty() and format.is_interactive_format():
terminal.clear_row()
print(textwrap.fill(_(BLAME_INFO_TEXT) + ":", width=terminal.get_size()[0]) + "\n")
terminal.printb(terminal.ljust(_("Author"), 21) + terminal.rjust(_("Rows"), 10) + terminal.rjust(_("Stability"), 15) +
terminal.rjust(_("Age"), 13) + terminal.rjust(_("% in comments"), 20))
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(str(i[1].rows).rjust(10), 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:.2f}".format(100.0 * i[1].comments / i[1].rows).rjust(19))
def output_xml(self):
message_xml = "\t\t<message>" + _(BLAME_INFO_TEXT) + "</message>\n"
blame_xml = ""
for i in sorted(self.blame.get_summed_blames().items()):
author_email = self.changes.get_latest_email_by_author(i[0])
name_xml = "\t\t\t\t<name>" + i[0] + "</name>\n"
email_xml = "\t\t\t\t<email>" + author_email + "</email>\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"
stability_xml = ("\t\t\t\t<stability>" + "{0:.1f}".format(Blame.get_stability(i[0], i[1].rows,
self.changes)) + "</stability>\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>\n")
blame_xml += ("\t\t\t<author>\n" + name_xml + email_xml + gravatar_xml + rows_xml + stability_xml +
age_xml + percentage_in_comments_xml + "\t\t\t</author>\n")
print("\t<blame>\n" + message_xml + "\t\t<authors>\n" + blame_xml + "\t\t</authors>\n\t</blame>")

View File

@ -0,0 +1,189 @@
# 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, was found")
NO_COMMITED_FILES_TEXT = N_("No commited files with the specified extensions were found")
class ChangesOutput(Outputable):
def __init__(self, changes):
self.changes = changes
Outputable.__init__(self)
def output_html(self):
authorinfo_list = self.changes.get_authorinfo_list()
total_changes = 0.0
changes_xml = "<div><div class=\"box\">"
chart_data = ""
for i in authorinfo_list:
total_changes += authorinfo_list.get(i).insertions
total_changes += authorinfo_list.get(i).deletions
if authorinfo_list:
changes_xml += "<p>" + _(HISTORICAL_INFO_TEXT) + ".</p><div><table id=\"changes\" class=\"git\">"
changes_xml += "<thead><tr> <th>{0}</th> <th>{1}</th> <th>{2}</th> <th>{3}</th> <th>{4}</th>".format(
_("Author"), _("Commits"), _("Insertions"), _("Deletions"), _("% of changes"))
changes_xml += "</tr></thead><tbody>"
for i, entry in enumerate(sorted(authorinfo_list)):
authorinfo = authorinfo_list.get(entry)
percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
changes_xml += "<tr " + ("class=\"odd\">" if i % 2 == 1 else ">")
if format.get_selected() == "html":
changes_xml += "<td><img src=\"{0}\"/>{1}</td>".format(
gravatar.get_url(self.changes.get_latest_email_by_author(entry)), entry)
else:
changes_xml += "<td>" + entry + "</td>"
changes_xml += "<td>" + str(authorinfo.commits) + "</td>"
changes_xml += "<td>" + str(authorinfo.insertions) + "</td>"
changes_xml += "<td>" + str(authorinfo.deletions) + "</td>"
changes_xml += "<td>" + "{0:.2f}".format(percentage) + "</td>"
changes_xml += "</tr>"
chart_data += "{{label: {0}, data: {1}}}".format(json.dumps(entry), "{0:.2f}".format(percentage))
if sorted(authorinfo_list)[-1] != entry:
chart_data += ", "
changes_xml += ("<tfoot><tr> <td colspan=\"5\">&nbsp;</td> </tr></tfoot></tbody></table>")
changes_xml += "<div class=\"chart\" id=\"changes_chart\"></div></div>"
changes_xml += "<script type=\"text/javascript\">"
changes_xml += " changes_plot = $.plot($(\"#changes_chart\"), [{0}], {{".format(chart_data)
changes_xml += " series: {"
changes_xml += " pie: {"
changes_xml += " innerRadius: 0.4,"
changes_xml += " show: true,"
changes_xml += " combine: {"
changes_xml += " threshold: 0.01,"
changes_xml += " label: \"" + _("Minor Authors") + "\""
changes_xml += " }"
changes_xml += " }"
changes_xml += " }, grid: {"
changes_xml += " hoverable: true"
changes_xml += " }"
changes_xml += " });"
changes_xml += "</script>"
else:
changes_xml += "<p>" + _(NO_COMMITED_FILES_TEXT) + ".</p>"
changes_xml += "</div></div>"
print(changes_xml)
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\": \"" + _(HISTORICAL_INFO_TEXT) + "\",\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):
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(_(HISTORICAL_INFO_TEXT) + ":", 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) + ".")
def output_xml(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_xml = "\t\t<message>" + _(HISTORICAL_INFO_TEXT) + "</message>\n"
changes_xml = ""
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_xml = "\t\t\t\t<name>" + i + "</name>\n"
email_xml = "\t\t\t\t<email>" + author_email + "</email>\n"
gravatar_xml = "\t\t\t\t<gravatar>" + gravatar.get_url(author_email) + "</gravatar>\n"
commits_xml = "\t\t\t\t<commits>" + str(authorinfo.commits) + "</commits>\n"
insertions_xml = "\t\t\t\t<insertions>" + str(authorinfo.insertions) + "</insertions>\n"
deletions_xml = "\t\t\t\t<deletions>" + str(authorinfo.deletions) + "</deletions>\n"
percentage_xml = "\t\t\t\t<percentage-of-changes>" + "{0:.2f}".format(percentage) + "</percentage-of-changes>\n"
changes_xml += ("\t\t\t<author>\n" + name_xml + email_xml + gravatar_xml + commits_xml +
insertions_xml + deletions_xml + percentage_xml + "\t\t\t</author>\n")
print("\t<changes>\n" + message_xml + "\t\t<authors>\n" + changes_xml + "\t\t</authors>\n\t</changes>")
else:
print("\t<changes>\n\t\t<exception>" + _(NO_COMMITED_FILES_TEXT) + "</exception>\n\t</changes>")

View File

@ -0,0 +1,97 @@
# 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 textwrap
from ..localization import N_
from .. import extensions, terminal
from .outputable import Outputable
EXTENSIONS_INFO_TEXT = N_("The extensions below were found in the repository history")
EXTENSIONS_MARKED_TEXT = N_("(extensions used during statistical analysis are marked)")
class ExtensionsOutput(Outputable):
@staticmethod
def is_marked(extension):
if extension in extensions.__extensions__ or "**" in extensions.__extensions__:
return True
return False
def output_html(self):
if extensions.__located_extensions__:
extensions_xml = "<div><div class=\"box\">"
extensions_xml += "<p>{0} {1}.</p><p>".format(_(EXTENSIONS_INFO_TEXT), _(EXTENSIONS_MARKED_TEXT))
for i in sorted(extensions.__located_extensions__):
if ExtensionsOutput.is_marked(i):
extensions_xml += "<strong>" + i + "</strong>"
else:
extensions_xml += i
extensions_xml += " "
extensions_xml += "</p></div></div>"
print(extensions_xml)
def output_json(self):
if extensions.__located_extensions__:
message_json = "\t\t\t\"message\": \"" + _(EXTENSIONS_INFO_TEXT) + "\",\n"
used_extensions_json = ""
unused_extensions_json = ""
for i in sorted(extensions.__located_extensions__):
if ExtensionsOutput.is_marked(i):
used_extensions_json += "\"" + i + "\", "
else:
unused_extensions_json += "\"" + i + "\", "
used_extensions_json = used_extensions_json[:-2]
unused_extensions_json = unused_extensions_json[:-2]
print(",\n\t\t\"extensions\": {\n" + message_json + "\t\t\t\"used\": [ " + used_extensions_json +
" ],\n\t\t\t\"unused\": [ " + unused_extensions_json + " ]\n" + "\t\t}", end="")
def output_text(self):
if extensions.__located_extensions__:
print("\n" + textwrap.fill("{0} {1}:".format(_(EXTENSIONS_INFO_TEXT), _(EXTENSIONS_MARKED_TEXT)),
width=terminal.get_size()[0]))
for i in sorted(extensions.__located_extensions__):
if ExtensionsOutput.is_marked(i):
print("[" + terminal.__bold__ + i + terminal.__normal__ + "]", end=" ")
else:
print (i, end=" ")
print("")
def output_xml(self):
if extensions.__located_extensions__:
message_xml = "\t\t<message>" + _(EXTENSIONS_INFO_TEXT) + "</message>\n"
used_extensions_xml = ""
unused_extensions_xml = ""
for i in sorted(extensions.__located_extensions__):
if ExtensionsOutput.is_marked(i):
used_extensions_xml += "\t\t\t<extension>" + i + "</extension>\n"
else:
unused_extensions_xml += "\t\t\t<extension>" + i + "</extension>\n"
print("\t<extensions>\n" + message_xml + "\t\t<used>\n" + used_extensions_xml + "\t\t</used>\n" +
"\t\t<unused>\n" + unused_extensions_xml + "\t\t</unused>\n" + "\t</extensions>")

View File

@ -0,0 +1,121 @@
# 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 textwrap
from ..localization import N_
from ..filtering import __filters__, has_filtered
from .. import terminal
from .outputable import Outputable
FILTERING_INFO_TEXT = N_("The following files were excluded from the statistics due to the specified exclusion patterns")
FILTERING_AUTHOR_INFO_TEXT = N_("The following authors were excluded from the statistics due to the specified exclusion patterns")
FILTERING_EMAIL_INFO_TEXT = N_("The authors with the following emails were excluded from the statistics due to the specified " \
"exclusion patterns")
FILTERING_COMMIT_INFO_TEXT = N_("The following commit revisions were excluded from the statistics due to the specified " \
"exclusion patterns")
class FilteringOutput(Outputable):
@staticmethod
def __output_html_section__(info_string, filtered):
filtering_xml = ""
if filtered:
filtering_xml += "<p>" + info_string + "."+ "</p>"
for i in filtered:
filtering_xml += "<p>" + i + "</p>"
return filtering_xml
def output_html(self):
if has_filtered():
filtering_xml = "<div><div class=\"box\">"
FilteringOutput.__output_html_section__(_(FILTERING_INFO_TEXT), __filters__["file"][1])
FilteringOutput.__output_html_section__(_(FILTERING_AUTHOR_INFO_TEXT), __filters__["author"][1])
FilteringOutput.__output_html_section__(_(FILTERING_EMAIL_INFO_TEXT), __filters__["email"][1])
FilteringOutput.__output_html_section__(_(FILTERING_COMMIT_INFO_TEXT), __filters__["revision"][1])
filtering_xml += "</div></div>"
print(filtering_xml)
@staticmethod
def __output_json_section__(info_string, filtered, container_tagname):
if filtered:
message_json = "\t\t\t\t\"message\": \"" + info_string + "\",\n"
filtering_json = ""
for i in filtered:
filtering_json += "\t\t\t\t\t\"" + i + "\",\n"
else:
filtering_json = filtering_json[:-3]
return "\n\t\t\t\"{0}\": {{\n".format(container_tagname) + message_json + \
"\t\t\t\t\"entries\": [\n" + filtering_json + "\"\n\t\t\t\t]\n\t\t\t},"
return ""
def output_json(self):
if has_filtered():
output = ",\n\t\t\"filtering\": {"
output += FilteringOutput.__output_json_section__(_(FILTERING_INFO_TEXT), __filters__["file"][1], "files")
output += FilteringOutput.__output_json_section__(_(FILTERING_AUTHOR_INFO_TEXT), __filters__["author"][1], "authors")
output += FilteringOutput.__output_json_section__(_(FILTERING_EMAIL_INFO_TEXT), __filters__["email"][1], "emails")
output += FilteringOutput.__output_json_section__(_(FILTERING_COMMIT_INFO_TEXT), __filters__["revision"][1], "revision")
output = output[:-1]
output += "\n\t\t}"
print(output, end="")
@staticmethod
def __output_text_section__(info_string, filtered):
if filtered:
print("\n" + textwrap.fill(info_string + ":", width=terminal.get_size()[0]))
for i in filtered:
(width, _unused) = terminal.get_size()
print("...%s" % i[-width+3:] if len(i) > width else i)
def output_text(self):
FilteringOutput.__output_text_section__(_(FILTERING_INFO_TEXT), __filters__["file"][1])
FilteringOutput.__output_text_section__(_(FILTERING_AUTHOR_INFO_TEXT), __filters__["author"][1])
FilteringOutput.__output_text_section__(_(FILTERING_EMAIL_INFO_TEXT), __filters__["email"][1])
FilteringOutput.__output_text_section__(_(FILTERING_COMMIT_INFO_TEXT), __filters__["revision"][1])
@staticmethod
def __output_xml_section__(info_string, filtered, container_tagname):
if filtered:
message_xml = "\t\t\t<message>" + info_string + "</message>\n"
filtering_xml = ""
for i in filtered:
filtering_xml += "\t\t\t\t<entry>" + i + "</entry>\n"
print("\t\t<{0}>".format(container_tagname))
print(message_xml + "\t\t\t<entries>\n" + filtering_xml + "\t\t\t</entries>\n")
print("\t\t</{0}>".format(container_tagname))
def output_xml(self):
if has_filtered():
print("\t<filtering>")
FilteringOutput.__output_xml_section__(_(FILTERING_INFO_TEXT), __filters__["file"][1], "files")
FilteringOutput.__output_xml_section__(_(FILTERING_AUTHOR_INFO_TEXT), __filters__["author"][1], "authors")
FilteringOutput.__output_xml_section__(_(FILTERING_EMAIL_INFO_TEXT), __filters__["email"][1], "emails")
FilteringOutput.__output_xml_section__(_(FILTERING_COMMIT_INFO_TEXT), __filters__["revision"][1], "revision")
print("\t</filtering>")

View File

@ -0,0 +1,160 @@
# 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
from ..changes import FileDiff
from ..localization import N_
from ..metrics import (__metric_eloc__, METRIC_CYCLOMATIC_COMPLEXITY_THRESHOLD, METRIC_CYCLOMATIC_COMPLEXITY_DENSITY_THRESHOLD)
from .outputable import Outputable
ELOC_INFO_TEXT = N_("The following files are suspiciously big (in order of severity)")
CYCLOMATIC_COMPLEXITY_TEXT = N_("The following files have an elevated cyclomatic complexity (in order of severity)")
CYCLOMATIC_COMPLEXITY_DENSITY_TEXT = N_("The following files have an elevated cyclomatic complexity density " \
"(in order of severity)")
METRICS_MISSING_INFO_TEXT = N_("No metrics violations were found in the repository")
METRICS_VIOLATION_SCORES = [[1.0, "minimal"], [1.25, "minor"], [1.5, "medium"], [2.0, "bad"], [3.0, "severe"]]
def __get_metrics_score__(ceiling, value):
for i in reversed(METRICS_VIOLATION_SCORES):
if value > ceiling * i[0]:
return i[1]
class MetricsOutput(Outputable):
def __init__(self, metrics):
self.metrics = metrics
Outputable.__init__(self)
def output_text(self):
if not self.metrics.eloc and not self.metrics.cyclomatic_complexity and not self.metrics.cyclomatic_complexity_density:
print("\n" + _(METRICS_MISSING_INFO_TEXT) + ".")
if self.metrics.eloc:
print("\n" + _(ELOC_INFO_TEXT) + ":")
for i in sorted(set([(j, i) for (i, j) in self.metrics.eloc.items()]), reverse=True):
print(_("{0} ({1} estimated lines of code)").format(i[1], str(i[0])))
if self.metrics.cyclomatic_complexity:
print("\n" + _(CYCLOMATIC_COMPLEXITY_TEXT) + ":")
for i in sorted(set([(j, i) for (i, j) in self.metrics.cyclomatic_complexity.items()]), reverse=True):
print(_("{0} ({1} in cyclomatic complexity)").format(i[1], str(i[0])))
if self.metrics.cyclomatic_complexity_density:
print("\n" + _(CYCLOMATIC_COMPLEXITY_DENSITY_TEXT) + ":")
for i in sorted(set([(j, i) for (i, j) in self.metrics.cyclomatic_complexity_density.items()]), reverse=True):
print(_("{0} ({1:.3f} in cyclomatic complexity density)").format(i[1], i[0]))
def output_html(self):
metrics_xml = "<div><div class=\"box\" id=\"metrics\">"
if not self.metrics.eloc and not self.metrics.cyclomatic_complexity and not self.metrics.cyclomatic_complexity_density:
metrics_xml += "<p>" + _(METRICS_MISSING_INFO_TEXT) + ".</p>"
if self.metrics.eloc:
metrics_xml += "<div><h4>" + _(ELOC_INFO_TEXT) + ".</h4>"
for num, i in enumerate(sorted(set([(j, i) for (i, j) in self.metrics.eloc.items()]), reverse=True)):
metrics_xml += "<div class=\"" + __get_metrics_score__(__metric_eloc__[FileDiff.get_extension(i[1])], i[0]) + \
(" odd\">" if num % 2 == 1 else "\">") + \
_("{0} ({1} estimated lines of code)").format(i[1], str(i[0])) + "</div>"
metrics_xml += "</div>"
if self.metrics.cyclomatic_complexity:
metrics_xml += "<div><h4>" + _(CYCLOMATIC_COMPLEXITY_TEXT) + "</h4>"
for num, i in enumerate(sorted(set([(j, i) for (i, j) in self.metrics.cyclomatic_complexity.items()]), reverse=True)):
metrics_xml += "<div class=\"" + __get_metrics_score__(METRIC_CYCLOMATIC_COMPLEXITY_THRESHOLD, i[0]) + \
(" odd\">" if num % 2 == 1 else "\">") + \
_("{0} ({1} in cyclomatic complexity)").format(i[1], str(i[0])) + "</div>"
metrics_xml += "</div>"
if self.metrics.cyclomatic_complexity_density:
metrics_xml += "<div><h4>" + _(CYCLOMATIC_COMPLEXITY_DENSITY_TEXT) + "</h4>"
for num, i in enumerate(sorted(set([(j, i) for (i, j) in self.metrics.cyclomatic_complexity_density.items()]), reverse=True)):
metrics_xml += "<div class=\"" + __get_metrics_score__(METRIC_CYCLOMATIC_COMPLEXITY_DENSITY_THRESHOLD, i[0]) + \
(" odd\">" if num % 2 == 1 else "\">") + \
_("{0} ({1:.3f} in cyclomatic complexity density)").format(i[1], i[0]) + "</div>"
metrics_xml += "</div>"
metrics_xml += "</div></div>"
print(metrics_xml)
def output_json(self):
if not self.metrics.eloc and not self.metrics.cyclomatic_complexity and not self.metrics.cyclomatic_complexity_density:
print(",\n\t\t\"metrics\": {\n\t\t\t\"message\": \"" + _(METRICS_MISSING_INFO_TEXT) + "\"\n\t\t}", end="")
else:
eloc_json = ""
if self.metrics.eloc:
for i in sorted(set([(j, i) for (i, j) in self.metrics.eloc.items()]), reverse=True):
eloc_json += "{\n\t\t\t\t\"type\": \"estimated-lines-of-code\",\n"
eloc_json += "\t\t\t\t\"file_name\": \"" + i[1] + "\",\n"
eloc_json += "\t\t\t\t\"value\": " + str(i[0]) + "\n"
eloc_json += "\t\t\t},"
else:
if not self.metrics.cyclomatic_complexity:
eloc_json = eloc_json[:-1]
if self.metrics.cyclomatic_complexity:
for i in sorted(set([(j, i) for (i, j) in self.metrics.cyclomatic_complexity.items()]), reverse=True):
eloc_json += "{\n\t\t\t\t\"type\": \"cyclomatic-complexity\",\n"
eloc_json += "\t\t\t\t\"file_name\": \"" + i[1] + "\",\n"
eloc_json += "\t\t\t\t\"value\": " + str(i[0]) + "\n"
eloc_json += "\t\t\t},"
else:
if not self.metrics.cyclomatic_complexity_density:
eloc_json = eloc_json[:-1]
if self.metrics.cyclomatic_complexity_density:
for i in sorted(set([(j, i) for (i, j) in self.metrics.cyclomatic_complexity_density.items()]), reverse=True):
eloc_json += "{\n\t\t\t\t\"type\": \"cyclomatic-complexity-density\",\n"
eloc_json += "\t\t\t\t\"file_name\": \"" + i[1] + "\",\n"
eloc_json += "\t\t\t\t\"value\": {0:.3f}\n".format(i[0])
eloc_json += "\t\t\t},"
else:
eloc_json = eloc_json[:-1]
print(",\n\t\t\"metrics\": {\n\t\t\t\"violations\": [\n\t\t\t" + eloc_json + "]\n\t\t}", end="")
def output_xml(self):
if not self.metrics.eloc and not self.metrics.cyclomatic_complexity and not self.metrics.cyclomatic_complexity_density:
print("\t<metrics>\n\t\t<message>" + _(METRICS_MISSING_INFO_TEXT) + "</message>\n\t</metrics>")
else:
eloc_xml = ""
if self.metrics.eloc:
for i in sorted(set([(j, i) for (i, j) in self.metrics.eloc.items()]), reverse=True):
eloc_xml += "\t\t\t<estimated-lines-of-code>\n"
eloc_xml += "\t\t\t\t<file-name>" + i[1] + "</file-name>\n"
eloc_xml += "\t\t\t\t<value>" + str(i[0]) + "</value>\n"
eloc_xml += "\t\t\t</estimated-lines-of-code>\n"
if self.metrics.cyclomatic_complexity:
for i in sorted(set([(j, i) for (i, j) in self.metrics.cyclomatic_complexity.items()]), reverse=True):
eloc_xml += "\t\t\t<cyclomatic-complexity>\n"
eloc_xml += "\t\t\t\t<file-name>" + i[1] + "</file-name>\n"
eloc_xml += "\t\t\t\t<value>" + str(i[0]) + "</value>\n"
eloc_xml += "\t\t\t</cyclomatic-complexity>\n"
if self.metrics.cyclomatic_complexity_density:
for i in sorted(set([(j, i) for (i, j) in self.metrics.cyclomatic_complexity_density.items()]), reverse=True):
eloc_xml += "\t\t\t<cyclomatic-complexity-density>\n"
eloc_xml += "\t\t\t\t<file-name>" + i[1] + "</file-name>\n"
eloc_xml += "\t\t\t\t<value>{0:.3f}</value>\n".format(i[0])
eloc_xml += "\t\t\t</cyclomatic-complexity-density>\n"
print("\t<metrics>\n\t\t<violations>\n" + eloc_xml + "\t\t</violations>\n\t</metrics>")

View File

@ -19,21 +19,26 @@
from __future__ import print_function
from __future__ import unicode_literals
import format
from .. import format
class Outputable(object):
def output_html(self):
print(_("HTML output not yet supported in") + " \"" + self.__class__.__name__ + "\".")
raise NotImplementedError(_("HTML output not yet supported in") + " \"" + self.__class__.__name__ + "\".")
def output_json(self):
raise NotImplementedError(_("JSON output not yet supported in") + " \"" + self.__class__.__name__ + "\".")
def output_text(self):
print(_("Text output not yet supported in") + " \"" + self.__class__.__name__ + "\".")
raise NotImplementedError(_("Text output not yet supported in") + " \"" + self.__class__.__name__ + "\".")
def output_xml(self):
print(_("XML output not yet supported in") + " \"" + self.__class__.__name__ + "\".")
raise NotImplementedError(_("XML output not yet supported in") + " \"" + self.__class__.__name__ + "\".")
def output(outputable):
if format.get_selected() == "html" or format.get_selected() == "htmlembedded":
outputable.output_html()
elif format.get_selected() == "json":
outputable.output_json()
elif format.get_selected() == "text":
outputable.output_text()
else:

View File

@ -0,0 +1,143 @@
# 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 textwrap
from ..localization import N_
from .. import format, gravatar, terminal
from .. import responsibilities as resp
from .outputable import Outputable
RESPONSIBILITIES_INFO_TEXT = N_("The following responsibilities, by author, were found in the current "
"revision of the repository (comments are excluded from the line count, "
"if possible)")
MOSTLY_RESPONSIBLE_FOR_TEXT = N_("is mostly responsible for")
class ResponsibilitiesOutput(Outputable):
def __init__(self, changes, blame):
self.changes = changes
self.blame = blame
Outputable.__init__(self)
def output_text(self):
print("\n" + textwrap.fill(_(RESPONSIBILITIES_INFO_TEXT) + ":", width=terminal.get_size()[0]))
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.blame, i)), reverse=True)
if responsibilities:
print("\n" + i, _(MOSTLY_RESPONSIBLE_FOR_TEXT) + ":")
for j, entry in enumerate(responsibilities):
(width, _unused) = terminal.get_size()
width -= 7
print(str(entry[0]).rjust(6), end=" ")
print("...%s" % entry[1][-width+3:] if len(entry[1]) > width else entry[1])
if j >= 9:
break
def output_html(self):
resp_xml = "<div><div class=\"box\" id=\"responsibilities\">"
resp_xml += "<p>" + _(RESPONSIBILITIES_INFO_TEXT) + ".</p>"
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.blame, i)), reverse=True)
if responsibilities:
resp_xml += "<div>"
if format.get_selected() == "html":
author_email = self.changes.get_latest_email_by_author(i)
resp_xml += "<h3><img src=\"{0}\"/>{1} {2}</h3>".format(gravatar.get_url(author_email, size=32),
i, _(MOSTLY_RESPONSIBLE_FOR_TEXT))
else:
resp_xml += "<h3>{0} {1}</h3>".format(i, _(MOSTLY_RESPONSIBLE_FOR_TEXT))
for j, entry in enumerate(responsibilities):
resp_xml += "<div" + (" class=\"odd\">" if j % 2 == 1 else ">") + entry[1] + \
" (" + str(entry[0]) + " eloc)</div>"
if j >= 9:
break
resp_xml += "</div>"
resp_xml += "</div></div>"
print(resp_xml)
def output_json(self):
message_json = "\t\t\t\"message\": \"" + _(RESPONSIBILITIES_INFO_TEXT) + "\",\n"
resp_json = ""
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.blame, i)), reverse=True)
if responsibilities:
author_email = self.changes.get_latest_email_by_author(i)
resp_json += "{\n"
resp_json += "\t\t\t\t\"name\": \"" + i + "\",\n"
resp_json += "\t\t\t\t\"email\": \"" + author_email + "\",\n"
resp_json += "\t\t\t\t\"gravatar\": \"" + gravatar.get_url(author_email) + "\",\n"
resp_json += "\t\t\t\t\"files\": [\n\t\t\t\t"
for j, entry in enumerate(responsibilities):
resp_json += "{\n"
resp_json += "\t\t\t\t\t\"name\": \"" + entry[1] + "\",\n"
resp_json += "\t\t\t\t\t\"rows\": " + str(entry[0]) + "\n"
resp_json += "\t\t\t\t},"
if j >= 9:
break
resp_json = resp_json[:-1]
resp_json += "]\n\t\t\t},"
resp_json = resp_json[:-1]
print(",\n\t\t\"responsibilities\": {\n" + message_json + "\t\t\t\"authors\": [\n\t\t\t" + resp_json + "]\n\t\t}", end="")
def output_xml(self):
message_xml = "\t\t<message>" + _(RESPONSIBILITIES_INFO_TEXT) + "</message>\n"
resp_xml = ""
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.blame, i)), reverse=True)
if responsibilities:
author_email = self.changes.get_latest_email_by_author(i)
resp_xml += "\t\t\t<author>\n"
resp_xml += "\t\t\t\t<name>" + i + "</name>\n"
resp_xml += "\t\t\t\t<email>" + author_email + "</email>\n"
resp_xml += "\t\t\t\t<gravatar>" + gravatar.get_url(author_email) + "</gravatar>\n"
resp_xml += "\t\t\t\t<files>\n"
for j, entry in enumerate(responsibilities):
resp_xml += "\t\t\t\t\t<file>\n"
resp_xml += "\t\t\t\t\t\t<name>" + entry[1] + "</name>\n"
resp_xml += "\t\t\t\t\t\t<rows>" + str(entry[0]) + "</rows>\n"
resp_xml += "\t\t\t\t\t</file>\n"
if j >= 9:
break
resp_xml += "\t\t\t\t</files>\n"
resp_xml += "\t\t\t</author>\n"
print("\t<responsibilities>\n" + message_xml + "\t\t<authors>\n" + resp_xml + "\t\t</authors>\n\t</responsibilities>")

View File

@ -0,0 +1,208 @@
# 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 textwrap
from ..localization import N_
from .. import format, gravatar, terminal, timeline
from .outputable import Outputable
TIMELINE_INFO_TEXT = N_("The following history timeline has been gathered from the repository")
MODIFIED_ROWS_TEXT = N_("Modified Rows:")
def __output_row__text__(timeline_data, periods, names):
print("\n" + terminal.__bold__ + terminal.ljust(_("Author"), 20), end=" ")
for period in periods:
print(terminal.rjust(period, 10), end=" ")
print(terminal.__normal__)
for name in names:
if timeline_data.is_author_in_periods(periods, name[0]):
print(terminal.ljust(name[0], 20)[0:20 - terminal.get_excess_column_count(name[0])], end=" ")
for period in periods:
multiplier = timeline_data.get_multiplier(period, 9)
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
signs_str = (signs[1] * "-" + signs[0] * "+")
print (("." if timeline_data.is_author_in_period(period, name[0]) and
len(signs_str) == 0 else signs_str).rjust(10), end=" ")
print("")
print(terminal.__bold__ + terminal.ljust(_(MODIFIED_ROWS_TEXT), 20) + terminal.__normal__, end=" ")
for period in periods:
total_changes = str(timeline_data.get_total_changes_in_period(period)[2])
if hasattr(total_changes, 'decode'):
total_changes = total_changes.decode("utf-8", "replace")
print(terminal.rjust(total_changes, 10), end=" ")
print("")
def __output_row__html__(timeline_data, periods, names):
timeline_xml = "<table class=\"git full\"><thead><tr><th>" + _("Author") + "</th>"
for period in periods:
timeline_xml += "<th>" + str(period) + "</th>"
timeline_xml += "</tr></thead><tbody>"
i = 0
for name in names:
if timeline_data.is_author_in_periods(periods, name[0]):
timeline_xml += "<tr" + (" class=\"odd\">" if i % 2 == 1 else ">")
if format.get_selected() == "html":
timeline_xml += "<td><img src=\"{0}\"/>{1}</td>".format(gravatar.get_url(name[1]), name[0])
else:
timeline_xml += "<td>" + name[0] + "</td>"
for period in periods:
multiplier = timeline_data.get_multiplier(period, 18)
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
signs_str = (signs[1] * "<div class=\"remove\">&nbsp;</div>" + signs[0] * "<div class=\"insert\">&nbsp;</div>")
timeline_xml += "<td>" + ("." if timeline_data.is_author_in_period(period, name[0]) and len(signs_str) == 0 else signs_str)
timeline_xml += "</td>"
timeline_xml += "</tr>"
i = i + 1
timeline_xml += "<tfoot><tr><td><strong>" + _(MODIFIED_ROWS_TEXT) + "</strong></td>"
for period in periods:
total_changes = timeline_data.get_total_changes_in_period(period)
timeline_xml += "<td>" + str(total_changes[2]) + "</td>"
timeline_xml += "</tr></tfoot></tbody></table>"
print(timeline_xml)
class TimelineOutput(Outputable):
def __init__(self, changes, useweeks):
self.changes = changes
self.useweeks = useweeks
Outputable.__init__(self)
def output_text(self):
if self.changes.get_commits():
print("\n" + textwrap.fill(_(TIMELINE_INFO_TEXT) + ":", width=terminal.get_size()[0]))
timeline_data = timeline.TimelineData(self.changes, self.useweeks)
periods = timeline_data.get_periods()
names = timeline_data.get_authors()
(width, _unused) = terminal.get_size()
max_periods_per_row = int((width - 21) / 11)
for i in range(0, len(periods), max_periods_per_row):
__output_row__text__(timeline_data, periods[i:i+max_periods_per_row], names)
def output_html(self):
if self.changes.get_commits():
timeline_data = timeline.TimelineData(self.changes, self.useweeks)
periods = timeline_data.get_periods()
names = timeline_data.get_authors()
max_periods_per_row = 8
timeline_xml = "<div><div id=\"timeline\" class=\"box\">"
timeline_xml += "<p>" + _(TIMELINE_INFO_TEXT) + ".</p>"
print(timeline_xml)
for i in range(0, len(periods), max_periods_per_row):
__output_row__html__(timeline_data, periods[i:i+max_periods_per_row], names)
timeline_xml = "</div></div>"
print(timeline_xml)
def output_json(self):
if self.changes.get_commits():
message_json = "\t\t\t\"message\": \"" + _(TIMELINE_INFO_TEXT) + "\",\n"
timeline_json = ""
periods_json = "\t\t\t\"period_length\": \"{0}\",\n".format("week" if self.useweeks else "month")
periods_json += "\t\t\t\"periods\": [\n\t\t\t"
timeline_data = timeline.TimelineData(self.changes, self.useweeks)
periods = timeline_data.get_periods()
names = timeline_data.get_authors()
for period in periods:
name_json = "\t\t\t\t\"name\": \"" + str(period) + "\",\n"
authors_json = "\t\t\t\t\"authors\": [\n\t\t\t\t"
for name in names:
if timeline_data.is_author_in_period(period, name[0]):
multiplier = timeline_data.get_multiplier(period, 24)
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
signs_str = (signs[1] * "-" + signs[0] * "+")
if len(signs_str) == 0:
signs_str = "."
authors_json += "{\n\t\t\t\t\t\"name\": \"" + name[0] + "\",\n"
authors_json += "\t\t\t\t\t\"email\": \"" + name[1] + "\",\n"
authors_json += "\t\t\t\t\t\"gravatar\": \"" + gravatar.get_url(name[1]) + "\",\n"
authors_json += "\t\t\t\t\t\"work\": \"" + signs_str + "\"\n\t\t\t\t},"
else:
authors_json = authors_json[:-1]
authors_json += "],\n"
modified_rows_json = "\t\t\t\t\"modified_rows\": " + \
str(timeline_data.get_total_changes_in_period(period)[2]) + "\n"
timeline_json += "{\n" + name_json + authors_json + modified_rows_json + "\t\t\t},"
else:
timeline_json = timeline_json[:-1]
print(",\n\t\t\"timeline\": {\n" + message_json + periods_json + timeline_json + "]\n\t\t}", end="")
def output_xml(self):
if self.changes.get_commits():
message_xml = "\t\t<message>" + _(TIMELINE_INFO_TEXT) + "</message>\n"
timeline_xml = ""
periods_xml = "\t\t<periods length=\"{0}\">\n".format("week" if self.useweeks else "month")
timeline_data = timeline.TimelineData(self.changes, self.useweeks)
periods = timeline_data.get_periods()
names = timeline_data.get_authors()
for period in periods:
name_xml = "\t\t\t\t<name>" + str(period) + "</name>\n"
authors_xml = "\t\t\t\t<authors>\n"
for name in names:
if timeline_data.is_author_in_period(period, name[0]):
multiplier = timeline_data.get_multiplier(period, 24)
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
signs_str = (signs[1] * "-" + signs[0] * "+")
if len(signs_str) == 0:
signs_str = "."
authors_xml += "\t\t\t\t\t<author>\n\t\t\t\t\t\t<name>" + name[0] + "</name>\n"
authors_xml += "\t\t\t\t\t\t<email>" + name[1] + "</email>\n"
authors_xml += "\t\t\t\t\t\t<gravatar>" + gravatar.get_url(name[1]) + "</gravatar>\n"
authors_xml += "\t\t\t\t\t\t<work>" + signs_str + "</work>\n\t\t\t\t\t</author>\n"
authors_xml += "\t\t\t\t</authors>\n"
modified_rows_xml = "\t\t\t\t<modified_rows>" + \
str(timeline_data.get_total_changes_in_period(period)[2]) + "</modified_rows>\n"
timeline_xml += "\t\t\t<period>\n" + name_xml + authors_xml + modified_rows_xml + "\t\t\t</period>\n"
print("\t<timeline>\n" + message_xml + periods_xml + timeline_xml + "\t\t</periods>\n\t</timeline>")

View File

@ -1,6 +1,6 @@
# coding: utf-8
#
# Copyright © 2012-2013 Ejwa Software. All rights reserved.
# Copyright © 2012-2015 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -19,110 +19,19 @@
from __future__ import print_function
from __future__ import unicode_literals
from localization import N_
from outputable import Outputable
import blame
import changes
import format
import gravatar
import terminal
import textwrap
class ResponsibiltyEntry:
class ResponsibiltyEntry(object):
blames = {}
class Responsibilities:
class Responsibilities(object):
@staticmethod
def get(hard, author_name):
def get(blame, author_name):
author_blames = {}
for i in blame.get(hard).blames.items():
if (author_name == i[0][0]):
for i in blame.blames.items():
if author_name == i[0][0]:
total_rows = i[1].rows - i[1].comments
if total_rows > 0:
author_blames[i[0][1]] = total_rows
return sorted(author_blames.items())
RESPONSIBILITIES_INFO_TEXT = N_("The following repsonsibilties, by author, were found in the current "
"revision of the repository (comments are exluded from the line count, "
"if possible)")
MOSTLY_RESPONSIBLE_FOR_TEXT = N_("is mostly responsible for")
class ResponsibilitiesOutput(Outputable):
def __init__(self, hard):
self.hard = hard
Outputable.__init__(self)
self.changes = changes.get(hard)
def output_text(self):
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).blames)):
responsibilities = sorted(((i[1], i[0]) for i in Responsibilities.get(self.hard, i)), reverse=True)
if responsibilities:
print("\n" + i, _(MOSTLY_RESPONSIBLE_FOR_TEXT) + ":")
for j, entry in enumerate(responsibilities):
(width, _unused) = terminal.get_size()
width -= 7
print(str(entry[0]).rjust(6), end=" ")
print("...%s" % entry[1][-width+3:] if len(entry[1]) > width else entry[1])
if j >= 9:
break
def output_html(self):
resp_xml = "<div><div class=\"box\" id=\"responsibilities\">"
resp_xml += "<p>" + _(RESPONSIBILITIES_INFO_TEXT) + ".</p>"
for i in sorted(set(i[0] for i in blame.get(self.hard).blames)):
responsibilities = sorted(((i[1], i[0]) for i in Responsibilities.get(self.hard, i)), reverse=True)
if responsibilities:
resp_xml += "<div>"
if format.get_selected() == "html":
author_email = self.changes.get_author_email(i)
resp_xml += "<h3><img src=\"{0}\"/>{1} {2}</h3>".format(gravatar.get_url(author_email, size=32),
i, _(MOSTLY_RESPONSIBLE_FOR_TEXT))
else:
resp_xml += "<h3>{0} {1}</h3>".format(i, _(MOSTLY_RESPONSIBLE_FOR_TEXT))
for j, entry in enumerate(responsibilities):
resp_xml += "<div" + (" class=\"odd\">" if j % 2 == 1 else ">") + entry[1] + \
" (" + str(entry[0]) + " eloc)</div>"
if j >= 9:
break
resp_xml += "</div>"
resp_xml += "</div></div>"
print(resp_xml)
def output_xml(self):
message_xml = "\t\t<message>" + _(RESPONSIBILITIES_INFO_TEXT) + "</message>\n"
resp_xml = ""
for i in sorted(set(i[0] for i in blame.get(self.hard).blames)):
responsibilities = sorted(((i[1], i[0]) for i in Responsibilities.get(self.hard, i)), reverse=True)
if responsibilities:
author_email = self.changes.get_author_email(i)
resp_xml += "\t\t\t<author>\n"
resp_xml += "\t\t\t\t<name>" + i + "</name>\n"
resp_xml += "\t\t\t\t<gravatar>" + gravatar.get_url(author_email) + "</gravatar>\n"
resp_xml += "\t\t\t\t<files>\n"
for j, entry in enumerate(responsibilities):
resp_xml += "\t\t\t\t\t<file>\n"
resp_xml += "\t\t\t\t\t\t<name>" + entry[1] + "</name>\n"
resp_xml += "\t\t\t\t\t\t<rows>" + str(entry[0]) + "</rows>\n"
resp_xml += "\t\t\t\t\t</file>\n"
if j >= 9:
break
resp_xml += "\t\t\t\t</files>\n"
resp_xml += "\t\t\t</author>\n"
print("\t<responsibilities>\n" + message_xml + "\t\t<authors>\n" + resp_xml + "\t\t</authors>\n\t</responsibilities>")

View File

@ -1,6 +1,6 @@
# coding: utf-8
#
# Copyright © 2012-2013 Ejwa Software. All rights reserved.
# Copyright © 2012-2015 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -22,9 +22,10 @@ import codecs
import os
import platform
import sys
import unicodedata
__bold__ = "\033[1m"
__normal__ = "\033[0;0m"
__bold__ = "\033[1m"
__normal__ = "\033[0;0m"
DEFAULT_TERMINAL_SIZE = (80, 25)
@ -76,7 +77,7 @@ def __get_size_linux__():
return int(size[1]), int(size[0])
def clear_row():
print("\b" * 200, end="")
print("\r", end="")
def skip_escapes(skip):
if skip:
@ -109,6 +110,10 @@ def set_stdout_encoding():
if not sys.stdout.isatty() and sys.version_info < (3,):
sys.stdout = codecs.getwriter("utf-8")(sys.stdout)
def set_stdin_encoding():
if not sys.stdin.isatty() and sys.version_info < (3,):
sys.stdin = codecs.getreader("utf-8")(sys.stdin)
def convert_command_line_to_utf8():
try:
argv = []
@ -119,3 +124,35 @@ def convert_command_line_to_utf8():
return argv
except AttributeError:
return sys.argv
def check_terminal_encoding():
if sys.stdout.isatty() and (sys.stdout.encoding == None or sys.stdin.encoding == None):
print(_("WARNING: The terminal encoding is not correctly configured. gitinspector might malfunction. "
"The encoding can be configured with the environment variable 'PYTHONIOENCODING'."), file=sys.stderr)
def get_excess_column_count(string):
width_mapping = {'F': 2, 'H': 1, 'W': 2, 'Na': 1, 'N': 1, 'A': 1}
result = 0
for i in string:
width = unicodedata.east_asian_width(i)
result += width_mapping[width]
return result - len(string)
def ljust(string, pad):
return string.ljust(pad - get_excess_column_count(string))
def rjust(string, pad):
return string.rjust(pad - get_excess_column_count(string))
def output_progress(text, pos, length):
if sys.stdout.isatty():
(width, _unused) = get_size()
progress_text = text.format(100 * pos / length)
if len(progress_text) > width:
progress_text = "...%s" % progress_text[-width+3:]
print("\r{0}\r{1}".format(" " * width, progress_text), end="")
sys.stdout.flush()

View File

@ -1,6 +1,6 @@
# coding: utf-8
#
# Copyright © 2012-2013 Ejwa Software. All rights reserved.
# Copyright © 2012-2015 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -17,17 +17,10 @@
# 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
from localization import N_
from outputable import Outputable
import datetime
import format
import gravatar
import terminal
import textwrap
class TimelineData:
class TimelineData(object):
def __init__(self, changes, useweeks):
authordateinfo_list = sorted(changes.get_authordateinfo_list().items())
self.changes = changes
@ -70,7 +63,7 @@ class TimelineData:
return self.total_changes_by_period[period]
def get_authors(self):
return sorted(set([(i[0][0], self.changes.get_author_email(i[0][0])) for i in self.entries.items()]))
return sorted(set([(i[0][0], self.changes.get_latest_email_by_author(i[0][0])) for i in self.entries.items()]))
def get_author_signs_in_period(self, author, period, multiplier):
authorinfo = self.entries.get((author, period), None)
@ -105,141 +98,3 @@ class TimelineData:
if self.is_author_in_period(period, author):
return True
return False
TIMELINE_INFO_TEXT = N_("The following history timeline has been gathered from the repository")
MODIFIED_ROWS_TEXT = N_("Modified Rows:")
def __output_row__text__(timeline_data, periods, names):
print("\n" + terminal.__bold__ + _("Author").ljust(20), end=" ")
for period in periods:
print(period.rjust(10), end=" ")
print(terminal.__normal__)
for name in names:
if timeline_data.is_author_in_periods(periods, name[0]):
print(name[0].ljust(20)[0:20], end=" ")
for period in periods:
multiplier = timeline_data.get_multiplier(period, 9)
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
signs_str = (signs[1] * "-" + signs[0] * "+")
print (("." if timeline_data.is_author_in_period(period, name[0]) and
len(signs_str) == 0 else signs_str).rjust(10), end=" ")
print("")
print(terminal.__bold__ + _(MODIFIED_ROWS_TEXT).ljust(20) + terminal.__normal__, end=" ")
for period in periods:
total_changes = timeline_data.get_total_changes_in_period(period)
print("" + str(total_changes[2]).rjust(10), end=" ")
print("")
def __output_row__html__(timeline_data, periods, names):
timeline_xml = "<table class=\"git full\"><thead><tr><th>" + _("Author") + "</th>"
for period in periods:
timeline_xml += "<th>" + str(period) + "</th>"
timeline_xml += "</tr></thead><tbody>"
i = 0
for name in names:
if timeline_data.is_author_in_periods(periods, name[0]):
timeline_xml += "<tr" + (" class=\"odd\">" if i % 2 == 1 else ">")
if format.get_selected() == "html":
timeline_xml += "<td><img src=\"{0}\"/>{1}</td>".format(gravatar.get_url(name[1]), name[0])
else:
timeline_xml += "<td>" + name[0] + "</td>"
for period in periods:
multiplier = timeline_data.get_multiplier(period, 18)
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
signs_str = (signs[1] * "<div class=\"remove\">&nbsp;</div>" + signs[0] * "<div class=\"insert\">&nbsp;</div>")
timeline_xml += "<td>" + ("." if timeline_data.is_author_in_period(period, name[0]) and len(signs_str) == 0 else signs_str)
timeline_xml += "</td>"
timeline_xml += "</tr>"
i = i + 1
timeline_xml += "<tfoot><tr><td><strong>" + _(MODIFIED_ROWS_TEXT) + "</strong></td>"
for period in periods:
total_changes = timeline_data.get_total_changes_in_period(period)
timeline_xml += "<td>" + str(total_changes[2]) + "</td>"
timeline_xml += "</tr></tfoot></tbody></table>"
print(timeline_xml)
class Timeline(Outputable):
def __init__(self, changes, useweeks):
self.changes = changes
self.useweeks = useweeks
Outputable.__init__(self)
def output_text(self):
if self.changes.get_commits():
print("\n" + textwrap.fill(_(TIMELINE_INFO_TEXT) + ":", width=terminal.get_size()[0]))
timeline_data = TimelineData(self.changes, self.useweeks)
periods = timeline_data.get_periods()
names = timeline_data.get_authors()
(width, _unused) = terminal.get_size()
max_periods_per_row = int((width - 21) / 11)
for i in range(0, len(periods), max_periods_per_row):
__output_row__text__(timeline_data, periods[i:i+max_periods_per_row], names)
def output_html(self):
if self.changes.get_commits():
timeline_data = TimelineData(self.changes, self.useweeks)
periods = timeline_data.get_periods()
names = timeline_data.get_authors()
max_periods_per_row = 8
timeline_xml = "<div><div id=\"timeline\" class=\"box\">"
timeline_xml += "<p>" + _(TIMELINE_INFO_TEXT) + ".</p>"
print(timeline_xml)
for i in range(0, len(periods), max_periods_per_row):
__output_row__html__(timeline_data, periods[i:i+max_periods_per_row], names)
timeline_xml = "</div></div>"
print(timeline_xml)
def output_xml(self):
if self.changes.get_commits():
message_xml = "\t\t<message>" + _(TIMELINE_INFO_TEXT) + "</message>\n"
timeline_xml = ""
periods_xml = "\t\t<periods length=\"{0}\">\n".format("week" if self.useweeks else "month")
timeline_data = TimelineData(self.changes, self.useweeks)
periods = timeline_data.get_periods()
names = timeline_data.get_authors()
for period in periods:
name_xml = "\t\t\t\t<name>" + str(period) + "</name>\n"
authors_xml = "\t\t\t\t<authors>\n"
for name in names:
if timeline_data.is_author_in_period(period, name[0]):
multiplier = timeline_data.get_multiplier(period, 24)
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
signs_str = (signs[1] * "-" + signs[0] * "+")
if len(signs_str) == 0:
signs_str = "."
authors_xml += "\t\t\t\t\t<author>\n\t\t\t\t\t\t<name>" + name[0] + "</name>\n"
authors_xml += "\t\t\t\t\t\t<gravatar>" + gravatar.get_url(name[1]) + "</gravatar>\n"
authors_xml += "\t\t\t\t\t\t<work>" + signs_str + "</work>\n\t\t\t\t\t</author>\n"
authors_xml += "\t\t\t\t</authors>\n"
modified_rows_xml = "\t\t\t\t<modified_rows>" + \
str(timeline_data.get_total_changes_in_period(period)[2]) + "</modified_rows>\n"
timeline_xml += "\t\t\t<period>\n" + name_xml + authors_xml + modified_rows_xml + "\t\t\t</period>\n"
print("\t<timeline>\n" + message_xml + periods_xml + timeline_xml + "\t\t</periods>\n\t</timeline>")

View File

@ -1,4 +1,4 @@
# Copyright © 2012-2013 Ejwa Software. All rights reserved.
# Copyright © 2012-2015 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -14,62 +14,54 @@
#
# You should have received a copy of the GNU General Public License
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
#
# This file was generated with the following command:
#
# xgettext --no-wrap -s -w 140 --language=Python --keyword="N_"
# --no-location --output=messages.pot *.py
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: gitinspector 0.3.1\n"
"Project-Id-Version: gitinspector 0.5.0dev\n"
"Report-Msgid-Bugs-To: gitinspector@ejwa.se\n"
"POT-Creation-Date: 2013-07-15 02:57+0200\n"
"POT-Creation-Date: 2015-10-02 03:35+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Adam Waldenberg <adam.waldenberg@ejwa.se>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "The extensions below were found in the repository history"
msgstr ""
msgid "(extensions used during statistical analysis are marked)"
msgstr ""
msgid "The following historical commit information, by author, was found in the repository"
msgstr ""
msgid "No commited files with the specified extensions were found"
msgstr ""
msgid "Author"
msgstr ""
msgid "Commits"
msgstr ""
msgid "Insertions"
msgstr ""
msgid "Deletions"
#, python-format
msgid "% in comments"
msgstr ""
#, python-format
msgid "% of changes"
msgstr ""
msgid "Minor Authors"
msgid "(extensions used during statistical analysis are marked)"
msgstr ""
msgid "specified output format not supported."
msgid "Age"
msgstr ""
msgid "Author"
msgstr ""
msgid "Below are the number of rows from each author that have survived and are still intact in the current revision"
msgstr ""
#, python-brace-format
msgid "Checking how many rows belong to each author (Progress): {0:.0f}%"
msgstr ""
msgid "Commits"
msgstr ""
msgid ""
"The following repsonsibilties, by author, were found in the current revision of the repository (comments are exluded from the "
"line count, if possible)"
msgstr ""
msgid "is mostly responsible for"
msgstr ""
msgid ""
"Copyright © 2012-2013 Ejwa Software. All rights reserved.\n"
"Copyright © 2012-2015 Ejwa Software. All rights reserved.\n"
"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n"
@ -77,88 +69,122 @@ msgid ""
"Written by Adam Waldenberg."
msgstr ""
msgid "gitinspector requires at least Python 2.6 to run (version {0} was found)."
msgid "Deletions"
msgstr ""
msgid "Try `{0} --help' for more information."
msgstr ""
msgid "The output has been generated by {0}; the statistical analysis tool for git repositories."
msgstr ""
msgid "Show minor authors"
msgstr ""
msgid "Hide minor authors"
msgstr ""
msgid "Show rows with minor work"
msgstr ""
msgid "Hide rows with minor work"
#, python-format
msgid "Error processing git repository at \"%s\"."
msgstr ""
msgid "HTML output not yet supported in"
msgstr ""
msgid "Text output not yet supported in"
msgid "Hide minor authors"
msgstr ""
msgid "XML output not yet supported in"
msgid "Hide rows with minor work"
msgstr ""
msgid ""
"The following files were missing in the repository and were therefore not completely included in the statistical analysis. To "
"include them, you can either checkout manually using git or use the -c option in gitinspector"
msgid "Insertions"
msgstr ""
msgid "The following files are suspiciously big (in order of severity)"
msgstr ""
msgid "No metrics violations were found in the repository"
msgstr ""
msgid "The following history timeline has been gathered from the repository"
msgid "Minor Authors"
msgstr ""
msgid "Modified Rows:"
msgstr ""
msgid "Checking how many rows belong to each author (Progress): {0:.0f}%"
msgid "No commited files with the specified extensions were found"
msgstr ""
msgid "Below are the number of rows from each author that have survived and are still intact in the current revision"
msgid "No metrics violations were found in the repository"
msgstr ""
#, python-brace-format
msgid "Repository statistics for {0}"
msgstr ""
msgid "Rows"
msgstr ""
#, python-format
msgid "% in comments"
msgid "Show minor authors"
msgstr ""
msgid "The given option argument is not a valid boolean."
msgid "Show rows with minor work"
msgstr ""
msgid "invalid regular expression specified"
msgid "Stability"
msgstr ""
#, python-brace-format
msgid "Statistical information for the repository '{0}' was gathered on {1}."
msgstr ""
msgid "Text output not yet supported in"
msgstr ""
msgid "The authors with the following emails were excluded from the statistics due to the specified exclusion patterns"
msgstr ""
msgid "The extensions below were found in the repository history"
msgstr ""
msgid "The following authors were excluded from the statistics due to the specified exclusion patterns"
msgstr ""
msgid "The following commit revisions were excluded from the statistics due to the specified exclusion patterns"
msgstr ""
msgid "The following files are suspiciously big (in order of severity)"
msgstr ""
msgid "The following files have an elevated cyclomatic complexity (in order of severity)"
msgstr ""
msgid "The following files have an elevated cyclomatic complexity density (in order of severity)"
msgstr ""
msgid "The following files were excluded from the statistics due to the specified exclusion patterns"
msgstr ""
msgid "The following historical commit information, by author, was found in the repository"
msgstr ""
msgid "The following history timeline has been gathered from the repository"
msgstr ""
msgid "The following responsibilities, by author, were found in the current revision of the repository (comments are excluded from the line count, if possible)"
msgstr ""
msgid "The given option argument is not a valid boolean."
msgstr ""
#, python-brace-format
msgid "The output has been generated by {0} {1}. The statistical analysis tool for git repositories."
msgstr ""
#, python-brace-format
msgid "Try `{0} --help' for more information."
msgstr ""
msgid "Unable to determine absolute path of git repository."
msgstr ""
#, python-brace-format
msgid ""
"Usage: {0} [OPTION]... [DIRECTORY]\n"
"List information about the repository in DIRECTORY. If no directory is\n"
"specified, the current directory is used. If multiple directories are\n"
"given, information will be fetched from the last directory specified.\n"
"Usage: {0} [OPTION]... [REPOSITORY]\n"
"List information about the repository in REPOSITORY. If no repository is\n"
"specified, the current directory is used. If multiple repositories are\n"
"given, information will be fetched from the last repository specified.\n"
"\n"
"Mandatory arguments to long options are mandatory for short options too.\n"
"Boolean arguments can only be given to long options.\n"
" -c, --checkout-missing[=BOOL] try to checkout any missing files\n"
" -f, --file-types=EXTENSIONS a comma separated list of file extensions to\n"
" include when computing statistics. The\n"
" default extensions used are:\n"
" {1}\n"
" Specifying * includes files with no\n"
" extension, while ** includes all files\n"
" -F, --format=FORMAT define in which format output should be\n"
" generated; the default format is 'text' and\n"
" the available formats are:\n"
@ -185,9 +211,11 @@ msgid ""
" specific date\n"
" -w, --weeks[=BOOL] show all statistical information in weeks\n"
" instead of in months\n"
" -x, --exclude=PATTERN an exclusion pattern describing file names\n"
" that should be excluded from the statistics;\n"
" can be specified multiple times\n"
" -x, --exclude=PATTERN an exclusion pattern describing the file\n"
" paths, revisions, revisions with certain\n"
" commit messages, author names or author\n"
" emails that should be excluded from the\n"
" statistics; can be specified multiple times\n"
" -h, --help display this help and exit\n"
" --version output version information and exit\n"
"\n"
@ -198,3 +226,34 @@ msgid ""
"gitinspector requires that the git executable is available in your PATH.\n"
"Report gitinspector bugs to gitinspector@ejwa.se."
msgstr ""
msgid "WARNING: The terminal encoding is not correctly configured. gitinspector might malfunction. The encoding can be configured with the environment variable 'PYTHONIOENCODING'."
msgstr ""
msgid "XML output not yet supported in"
msgstr ""
#, python-brace-format
msgid "gitinspector requires at least Python 2.6 to run (version {0} was found)."
msgstr ""
msgid "invalid regular expression specified"
msgstr ""
msgid "is mostly responsible for"
msgstr ""
msgid "specified output format not supported."
msgstr ""
#, python-brace-format
msgid "{0} ({1:.3f} in cyclomatic complexity density)"
msgstr ""
#, python-brace-format
msgid "{0} ({1} estimated lines of code)"
msgstr ""
#, python-brace-format
msgid "{0} ({1} in cyclomatic complexity)"
msgstr ""

Binary file not shown.

View File

@ -0,0 +1,319 @@
# 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/>.
#
# Philipp Nowak <devnull@nowak-at.net>, 2014-2015
msgid ""
msgstr ""
"Project-Id-Version: gitinspector 0.5.0dev\n"
"Report-Msgid-Bugs-To: gitinspector@ejwa.se\n"
"POT-Creation-Date: 2015-10-02 03:35+0200\n"
"PO-Revision-Date: 2015-10-21 00:25+0200\n"
"Last-Translator: Philipp Nowak <devnull@nowak-at.net>\n"
"Language-Team: Deutsch <>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.8.5\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#, python-format
msgid "% in comments"
msgstr "% in Kommentaren"
# Poedit complains on validate when using a non-valid format specifier after the percent sign, even if separated by a space. It claims that argument number must be equal in both format and message, so can't use the word. Correct translation would be "% gesamt"
#, python-format
msgid "% of changes"
msgstr "% d. Änderungen"
msgid "(extensions used during statistical analysis are marked)"
msgstr "(Einbezogene markiert)"
msgid "Age"
msgstr "Alter"
msgid "Author"
msgstr "Autor"
# spaces because this needs to override the progress message displayed before
msgid "Below are the number of rows from each author that have survived and are still intact in the current revision"
msgstr "Noch intakte Codezeilen nach Autoren "
#, python-brace-format
msgid "Checking how many rows belong to each author (Progress): {0:.0f}%"
msgstr "Berechne intakte Zeilen pro Autor...{0:.0f}%"
msgid "Commits"
msgstr "Commits"
msgid ""
"Copyright © 2012-2015 Ejwa Software. All rights reserved.\n"
"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n"
"\n"
"Written by Adam Waldenberg."
msgstr ""
"Copyright © 2012-2015 Ejwa Software. Alle Rechte vorbehalten.\n"
"Lizensiert unter der GNU GPL Version 3 oder neuer\n"
"Dies ist freie Software, die Sie unter bestimmten Bedingungen weitergeben\n"
"dürfen.\n"
"\n"
"Für dieses Programm besteht KEINERLEI GARANTIE.\n"
"Siehe <http://gnu.org/licenses/gpl.html> für Details.\n"
"\n"
"Geschrieben von Adam Waldenberg."
msgid "Deletions"
msgstr "entfernt"
#, python-format
msgid "Error processing git repository at \"%s\"."
msgstr "Konnte Git Repository \"%s\" nicht verarbeiten."
msgid "HTML output not yet supported in"
msgstr "HTML-Ausgabe ist noch nicht unterstützt in"
msgid "Hide minor authors"
msgstr "Verstecke irrelevante Autoren"
msgid "Hide rows with minor work"
msgstr "Verstecke irrelevante Autoren"
msgid "Insertions"
msgstr "hinzugefügt"
msgid "Minor Authors"
msgstr "Sonstige"
msgid "Modified Rows:"
msgstr "Bearbeitete Zeilen:"
msgid "No commited files with the specified extensions were found"
msgstr "Keine eingecheckten Dateien mit den angegebenen Endungen gefunden"
msgid "No metrics violations were found in the repository"
msgstr "Im Repo wurde keine Verletzung der Metriken gefunden"
#, python-brace-format
msgid "Repository statistics for {0}"
msgstr "Repositorystatistiken für {0}"
msgid "Rows"
msgstr "Zeilen"
msgid "Show minor authors"
msgstr "Zeige irrelevante Autoren"
msgid "Show rows with minor work"
msgstr "Zeige irrelevante Autoren"
msgid "Stability"
msgstr "Stabilität"
#, python-brace-format
msgid "Statistical information for the repository '{0}' was gathered on {1}."
msgstr "Statistiken für das Repository '{0}' berechnet am {1}."
msgid "Text output not yet supported in"
msgstr "Textausgabe ist noch nicht unterstützt in"
msgid "The authors with the following emails were excluded from the statistics due to the specified exclusion patterns"
msgstr "Die Autoren mit den folgenden E-Mail-Adressen wurden von der Statistik ausgeschlossen, da sie zu den Ausschlussmustern passten"
msgid "The extensions below were found in the repository history"
msgstr "Gefundene Dateiendungen"
msgid "The following authors were excluded from the statistics due to the specified exclusion patterns"
msgstr "Die folgenden Autoren wurden von der Statistik ausgeschlossen, da sie zu den Ausschlussmustern passten"
msgid "The following commit revisions were excluded from the statistics due to the specified exclusion patterns"
msgstr "Die folgenden Änderungen wurden von der Statistik ausgeschlossen, da sie zu den Ausschlussmustern passten"
msgid "The following files are suspiciously big (in order of severity)"
msgstr "Die folgenden Dateien sind verdächtig groß (Geordnet nach Schweregrad)"
msgid "The following files have an elevated cyclomatic complexity (in order of severity)"
msgstr "Folgende Dateien haben eine hohe zyklomatische Komplexität (nach Schweregrad)"
msgid "The following files have an elevated cyclomatic complexity density (in order of severity)"
msgstr "Die folgenden Dateien haben eine sehr hohe McCabe-Metrik-Dichte (Geordnet nach Schweregrad)"
msgid "The following files were excluded from the statistics due to the specified exclusion patterns"
msgstr "Die folgenden Dateien wurden von der Statistik ausgeschlossen, da sie zu den Ausschlussmustern passten"
msgid "The following historical commit information, by author, was found in the repository"
msgstr "Commits nach Autoren"
msgid "The following history timeline has been gathered from the repository"
msgstr "Commitaktivität nach Autoren"
msgid "The following responsibilities, by author, were found in the current revision of the repository (comments are excluded from the line count, if possible)"
msgstr "Verantwortlichkeiten nach Autor (Kommentare nach Möglichkeit ignoriert)"
msgid "The given option argument is not a valid boolean."
msgstr "Das eingegebene Optionsargument ist kein valider Bool'scher Wert"
#, python-brace-format
msgid "The output has been generated by {0} {1}. The statistical analysis tool for git repositories."
msgstr "Die folgende Analyse wurde erstellt mit {0} {1}, dem statistischen Analysetool for Git-Repositories."
#, python-brace-format
msgid "Try `{0} --help' for more information."
msgstr "Verwende `{0} --help` für mehr Informationen."
msgid "Unable to determine absolute path of git repository."
msgstr "Konnte absoluten Pfad des Repos nicht finden!"
#, python-brace-format
msgid ""
"Usage: {0} [OPTION]... [REPOSITORY]\n"
"List information about the repository in REPOSITORY. If no repository is\n"
"specified, the current directory is used. If multiple repositories are\n"
"given, information will be fetched from the last repository specified.\n"
"\n"
"Mandatory arguments to long options are mandatory for short options too.\n"
"Boolean arguments can only be given to long options.\n"
" -f, --file-types=EXTENSIONS a comma separated list of file extensions to\n"
" include when computing statistics. The\n"
" default extensions used are:\n"
" {1}\n"
" Specifying * includes files with no\n"
" extension, while ** includes all files\n"
" -F, --format=FORMAT define in which format output should be\n"
" generated; the default format is 'text' and\n"
" the available formats are:\n"
" {2}\n"
" --grading[=BOOL] show statistics and information in a way that\n"
" is formatted for grading of student\n"
" projects; this is the same as supplying the\n"
" options -HlmrTw\n"
" -H, --hard[=BOOL] track rows and look for duplicates harder;\n"
" this can be quite slow with big repositories\n"
" -l, --list-file-types[=BOOL] list all the file extensions available in the\n"
" current branch of the repository\n"
" -L, --localize-output[=BOOL] localize the generated output to the selected\n"
" system language if a translation is\n"
" available\n"
" -m --metrics[=BOOL] include checks for certain metrics during the\n"
" analysis of commits\n"
" -r --responsibilities[=BOOL] show which files the different authors seem\n"
" most responsible for\n"
" --since=DATE only show statistics for commits more recent\n"
" than a specific date\n"
" -T, --timeline[=BOOL] show commit timeline, including author names\n"
" --until=DATE only show statistics for commits older than a\n"
" specific date\n"
" -w, --weeks[=BOOL] show all statistical information in weeks\n"
" instead of in months\n"
" -x, --exclude=PATTERN an exclusion pattern describing the file\n"
" paths, revisions, revisions with certain\n"
" commit messages, author names or author\n"
" emails that should be excluded from the\n"
" statistics; can be specified multiple times\n"
" -h, --help display this help and exit\n"
" --version output version information and exit\n"
"\n"
"gitinspector will filter statistics to only include commits that modify,\n"
"add or remove one of the specified extensions, see -f or --file-types for\n"
"more information.\n"
"\n"
"gitinspector requires that the git executable is available in your PATH.\n"
"Report gitinspector bugs to gitinspector@ejwa.se."
msgstr ""
"Verwendung: {0} [OPTION]... [REPOSITORY]\n"
"Zeigt Informationen über das Git-Repository in REPOSITORY. \n"
"Wenn kein Repository angegeben wurde, wird das aktuelle\n"
"Verzeichnis verwendet. Wenn mehrere angegeben sind,\n"
"wird das letzte verwendet.\n"
"\n"
"Erforderliche Argumente für Langschreibweisen der Optionensind auch\n"
"für Kurzschreibweisen erforderlich. Bool'sche Werte (true/false) können\n"
"nur mit Langschreibweisen verwendet werden.\n"
" -f, --file-types=ENDUNGEN Eine kommagetrennte Liste der\n"
" Dateiendungen die in die Analyse\n"
" einbezogen werden sollen. Standard:\n"
" {1}\n"
" * bezieht Dateien ohne Endung ein,\n"
" ** bezieht alle Dateien ein.\n"
" -F, --format=FORMAT Definiert das Ausgabeformat der\n"
" Analyse. Der Standard ist 'text', verfügbar:\n"
" {2}\n"
" --grading[=BOOL] Formatiert Statistiken so, dass\n"
" sie zur Bewertung von Schülern verwendet\n"
" werden können. Alias für -HlmrTw\n"
" -H, --hard[=BOOL] Verfolge Zeilen und suche genauer\n"
" nach Duplikaten; Kann bei großen\n"
" Repositories sehr lange dauern.\n"
" -l, --list-file-types[=BOOL] Zeige alle Datei-Endungen im aktuellen\n"
" Git Branch\n"
" -L, --localize-output[=BOOL] Übersetze Ausgabe in die Systemsprache,\n"
" wenn eine Übersetzung verfügbar ist.\n"
" -m --metrics[=BOOL] Beziehe spezielle Metriken in die Analyse von\n"
" Commits ein.\n"
" -r --responsibilities[=BOOL] Zeige, für welche Dateien die Autoren am\n"
" meisten verantwortlich zu sein scheinen.\n"
" -T, --timeline[=BOOL] Berechne Commit-Zeitstrahl (nach Autoren)\n"
" --since=DATE Analysiere nur ab einem bestimmten Datum\n"
" --until=DATE Analysiere nur vor einem bestimmten Datum\n"
" -w, --weeks[=BOOL] Stelle Statisiken in Wochen statt Monaten dar\n"
" -x, --exclude=PATTERN Ausschlussmuster für Dateipfade,\n"
" Commithashes, Commitnachrichten,\n"
" Autorennamen oder -E-Mails, die von\n"
" der Analyse ausgeschlossen werden\n"
" sollen; Kann mehrmals angegeben werden\n"
" -h, --help Zeige diese Nachricht und beende\n"
" --version Zeige Version und beende\n"
"\n"
"gitinspector zeigt nur Statistiken zu Commits, die Dateien\n"
"mit bestimmten Endungen bearbeiten, hinzufügen oder\n"
"löschen. Siehe -f oder --file-types für mehr informationen.\n"
"\n"
"gitinspector benötigt git in PATH.\n"
"Melde Bugs an gitinspector@ejwa.se."
msgid "WARNING: The terminal encoding is not correctly configured. gitinspector might malfunction. The encoding can be configured with the environment variable 'PYTHONIOENCODING'."
msgstr "WARNUNG: Das Terminal ist nicht richtig konfiguriert. Gitinspector könnte Probleme haben. Die Kodierung kann mit der Umgebungsvariable `PYTHONIOENCODING` konfiguriert werden."
msgid "XML output not yet supported in"
msgstr "XML-Ausgabe ist noch nicht unterstützt in"
#, python-brace-format
msgid "gitinspector requires at least Python 2.6 to run (version {0} was found)."
msgstr "Gitinspector benötigt zumindest Python 2.6 (Du verwendest {0})"
msgid "invalid regular expression specified"
msgstr "Invalider regulärer Ausdruck angegeben"
msgid "is mostly responsible for"
msgstr "ist am meisten verantwortlich für"
msgid "specified output format not supported."
msgstr "Das angegebene Ausgabeformat wird nicht unterstützt."
#, python-brace-format
msgid "{0} ({1:.3f} in cyclomatic complexity density)"
msgstr "{0} (zyklomatische Komplexitätsdichte: {1:.3f})"
#, python-brace-format
msgid "{0} ({1} estimated lines of code)"
msgstr "{0} ({1} geschätzte Codezeilen)"
#, python-brace-format
msgid "{0} ({1} in cyclomatic complexity)"
msgstr "{0} (zyklomatische Komplexität: {1})"

Binary file not shown.

View File

@ -0,0 +1,413 @@
# 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/>.
#
# Agustín Cañas <agustincanas@gmail.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: gitinspector 0.5.0dev\n"
"Report-Msgid-Bugs-To: gitinspector@ejwa.se\n"
"POT-Creation-Date: 2015-10-02 03:35+0200\n"
"PO-Revision-Date: 2015-10-02 22:17+0100\n"
"Last-Translator: Agustín Cañas <agustincanas@gmail.com>\n"
"Language-Team: Spanish <>\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#, python-format
msgid "% in comments"
msgstr "% en comentarios"
#, python-format
msgid "% of changes"
msgstr "% de cambios"
msgid "(extensions used during statistical analysis are marked)"
msgstr "(se marcan las extensiones usadas durante el análisis)"
msgid "Age"
msgstr "Edad"
msgid "Author"
msgstr "Autor"
msgid ""
"Below are the number of rows from each author that have survived and are "
"still intact in the current revision"
msgstr ""
"El bloque siguiente muestra el número de líneas de cada autor que todavía "
"permanecen intactas en la versión actual"
#, python-brace-format
msgid "Checking how many rows belong to each author (Progress): {0:.0f}%"
msgstr ""
"Comprobando cuantas líneas pertenecen a cada autor (Progreso): {0:.0f}%"
msgid "Commits"
msgstr "Commits"
msgid ""
"Copyright © 2012-2015 Ejwa Software. All rights reserved.\n"
"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl."
"html>.\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n"
"\n"
"Written by Adam Waldenberg."
msgstr ""
"Copyright © 2012-2015 Ejwa Software. Todos los derechos reservados.\n"
"Este programa es software libre. Puede redistribuirlo y/o modificarlo bajo\n"
"los términos de la Licencia Pública General de GNU tal como está publicada\n"
"por la Free Software Foundation, bien de la versión 3 de dicha Licencia o\n"
"bien (según su elección) de cualquier versión posterior.\n"
"Esta aplicación es software libre: usted es libre de modificarlo y \n"
"redistribuirlo.\n"
"Este programa se distribuye con la esperanza de que sea útil, pero SIN\n"
"NINGUNA GARANTÍA, incluso sin la garantía MERCANTIL implícita o sin\n"
"garantizar la CONVENIENCIA PARA UN PROPÓSITO PARTICULAR. Véase la Licencia \n"
"Pública General de GNU para más detalles <http://gnu.org/licenses/gpl."
"html>.\n"
"Escrito por Adam Waldenberg."
msgid "Deletions"
msgstr "Borrados"
#, python-format
msgid "Error processing git repository at \"%s\"."
msgstr "Error procesando el repositorio \"%s\""
msgid "HTML output not yet supported in"
msgstr "La salida en HTML no está soportada"
msgid "Hide minor authors"
msgstr "Ocultar los autores menos prolíficos"
msgid "Hide rows with minor work"
msgstr "Ocultar las filas con menor trabajo"
msgid "Insertions"
msgstr "Inserciones"
msgid "Minor Authors"
msgstr "Autores menos prolíficos"
msgid "Modified Rows:"
msgstr "Líneas modificadas:"
msgid "No commited files with the specified extensions were found"
msgstr "No se encontraron archivos comiteados con la extensión especificada"
msgid "No metrics violations were found in the repository"
msgstr "No se encontraron violaciones de la métrica en el repositorio"
#, python-brace-format
msgid "Repository statistics for {0}"
msgstr "Estadísticas del repositorio para {0}"
msgid "Rows"
msgstr "Registros"
msgid "Show minor authors"
msgstr "Mostrar los autores menos prolíficos"
msgid "Show rows with minor work"
msgstr "Mostrar las aportaciones menores"
msgid "Stability"
msgstr "Estabilidad"
#, python-brace-format
msgid "Statistical information for the repository '{0}' was gathered on {1}."
msgstr "Información estadística para el repositorio '{0}' obtenida en {1}."
msgid "Text output not yet supported in"
msgstr "La salida de tipo texto no está soportada"
msgid ""
"The authors with the following emails were excluded from the statistics due "
"to the specified exclusion patterns"
msgstr ""
"Los autores con las siguientes direcciones de correo fueron excluidos según "
"los patrones de exclusión especificados"
msgid "The extensions below were found in the repository history"
msgstr "En el repositorio se encontraron las siguientes extensiones"
msgid ""
"The following authors were excluded from the statistics due to the specified "
"exclusion patterns"
msgstr ""
"Los autores siguientes fueron excluidos de las estadísticas según los "
"patrones de exclusión especificados"
msgid ""
"The following commit revisions were excluded from the statistics due to the "
"specified exclusion patterns"
msgstr ""
"Los siguientes commits fueron excluidos de las estadísticas por los "
"patrones de exclusión especificados"
msgid "The following files are suspiciously big (in order of severity)"
msgstr "Los siguientes ficheros son sospechosamente grandes (listados en "
"orden de severidad)"
msgid ""
"The following files have an elevated cyclomatic complexity (in order of "
"severity)"
msgstr "Los siguientes ficheros tienen una elevada cyclomatic complexity "
"(listados en orden de severidad)"
msgid ""
"The following files have an elevated cyclomatic complexity density (in order "
"of severity)"
msgstr "Los siguientes ficheros tienen una elevada cyclomatic complexity "
"density (listados en orden de severidad)"
msgid ""
"The following files were excluded from the statistics due to the specified "
"exclusion patterns"
msgstr ""
"Los siguientes ficheros fueron excluidos de las estadísticas por los "
"patrones de exclusión especificados"
msgid ""
"The following historical commit information, by author, was found in the "
"repository"
msgstr "Se encontró en el repositorio el siguiente histórico de commits "
"(clasificado por autor)"
msgid "The following history timeline has been gathered from the repository"
msgstr ""
"Se ha obtenido del repositorio la siguiente línea temporal"
msgid ""
"The following responsibilities, by author, were found in the current revision "
"of the repository (comments are excluded from the line count, if possible)"
msgstr ""
"A continuación se presentan para cada autor los ficheros en los que ha "
"tenido mayor responsabilidad o impacto (en la medida de lo posible se han "
"excluido las líneas con comentarios)"
msgid "The given option argument is not a valid boolean."
msgstr "El argumento especificado no es de tipo booleano"
#, python-brace-format
msgid ""
"The output has been generated by {0} {1}. The statistical analysis tool for "
"git repositories."
msgstr ""
"La salida ha sido generada por {0} {1}. La herramienta de análisis "
"estadístico para repositorios git"
#, python-brace-format
msgid "Try `{0} --help' for more information."
msgstr "Teclea '{0} --help' para obtener más información"
msgid "Unable to determine absolute path of git repository."
msgstr "No se ha podido determinar la ruta absoluta al repositorio git"
#, python-brace-format
msgid ""
"Usage: {0} [OPTION]... [REPOSITORY]\n"
"List information about the repository in REPOSITORY. If no repository is\n"
"specified, the current directory is used. If multiple repositories are\n"
"given, information will be fetched from the last repository specified.\n"
"\n"
"Mandatory arguments to long options are mandatory for short options too.\n"
"Boolean arguments can only be given to long options.\n"
" -f, --file-types=EXTENSIONS a comma separated list of file extensions "
"to\n"
" include when computing statistics. The\n"
" default extensions used are:\n"
" {1}\n"
" -F, --format=FORMAT define in which format output should be\n"
" generated; the default format is 'text' "
"and\n"
" the available formats are:\n"
" {2}\n"
" --grading[=BOOL] show statistics and information in a way "
"that\n"
" is formatted for grading of student\n"
" projects; this is the same as supplying "
"the\n"
" options -HlmrTw\n"
" -H, --hard[=BOOL] track rows and look for duplicates harder;\n"
" this can be quite slow with big "
"repositories\n"
" -l, --list-file-types[=BOOL] list all the file extensions available in "
"the\n"
" current branch of the repository\n"
" -L, --localize-output[=BOOL] localize the generated output to the "
"selected\n"
" system language if a translation is\n"
" available\n"
" -m --metrics[=BOOL] include checks for certain metrics during "
"the\n"
" analysis of commits\n"
" -r --responsibilities[=BOOL] show which files the different authors "
"seem\n"
" most responsible for\n"
" --since=DATE only show statistics for commits more "
"recent\n"
" than a specific date\n"
" -T, --timeline[=BOOL] show commit timeline, including author "
"names\n"
" --until=DATE only show statistics for commits older than "
"a\n"
" specific date\n"
" -w, --weeks[=BOOL] show all statistical information in weeks\n"
" instead of in months\n"
" -x, --exclude=PATTERN an exclusion pattern describing the file\n"
" paths, revisions, author names or author\n"
" emails that should be excluded from the\n"
" statistics; can be specified multiple "
"times\n"
" -h, --help display this help and exit\n"
" --version output version information and exit\n"
"\n"
"gitinspector will filter statistics to only include commits that modify,\n"
"add or remove one of the specified extensions, see -f or --file-types for\n"
"more information.\n"
"\n"
"gitinspector requires that the git executable is available in your PATH.\n"
"Report gitinspector bugs to gitinspector@ejwa.se."
msgstr ""
"Uso: {0} [OPCIÓN]... [REPOSITORIO]\n"
"Lista información sobre el repositorio en REPOSITORIO. Si no se especifica \n"
"un repositorio se usará el directorio actual. Si se indican varios \n"
"repositorios, se extraerá la información del último de ellos\n"
"\n"
"Los argumentos obligatorios son necesarios tanto usando las opciones largas \n"
"como las cortas. Los argumentos de tipo booleano solo pueden combinarse \n"
"con opciones largas\n"
" -f, --file-types=EXTENSIONES una lista de extensiones (separadas por"
" \n"
" comas) que se incluirán al extraer "
"las \n"
" estadísticas. Se incluyen por "
"defecto:\n"
" {1}\n"
" Indicando * se incluirán ficheros "
"sin\n"
" extensión; con ** se incluirán todos"
"\n"
" -F, --format=FORMATO define el formato de salida que se "
"quiere \n"
" generar; el formato por defecto es \n"
" 'text' y los formatos disponibles "
"son:\n"
" {2}\n"
" --grading[=BOOLEANO] muestra estadísticas e información \n"
" formateada para valorar el trabajo "
"de\n"
" un estudiante; es equivalente a "
"usar\n"
" las opciones -HlmrTw\n"
" -H, --hard[=BOOLEANO] hace un seguimiento de las filas y "
"busca\n"
" duplicados; este análisis puede ser"
"\n"
" algo lento con repositorios grandes\n"
" -l, --list-file-types[=BOOLEANO] muestra todas las extensiones\n"
" disponibles en la rama actual del\n"
" repositorio\n"
" -L, --localize-output[=BOOLEANO] si está disponible, formatea la salida "
"en\n"
" el idioma del sistema\n"
" -m --metrics[=BOOLEANO] incluye comprobaciones para ciertas \n"
" métricas durante el análisis de los\n"
" commits\n"
" -r --responsibilities[=BOOLEANO] muestra para cada autor cuáles son "
"los\n"
" ficheros en los que parece tener "
"mayor\n"
" participación\n"
" --since=FECHA solo muestra estadísticas para los \n"
" commits realizados desde una fecha\n"
" determinada\n"
" -T, --timeline[=BOOLEANO] muestra los commits en la línea de\n"
" tiempo, autor y nombres\n"
" --until=FECHA solo muestra estadísticas para commits"
"\n"
" realizados hasta la fecha "
"especificada\n"
" -w, --weeks[=BOOLEANO] muestra la información estadística en\n"
" semanas en lugar de meses\n"
" -x, --exclude=PATRÓN un patrón de exclusión para las rutas "
"de\n"
" los ficheros, las revisiones, las\n"
" revisiones con ciertos mensajes de \n"
" commit, los nombres de los autores o "
"sus\n"
" direcciones de correo electrónico "
"que\n"
" se desea excluir; pueden indicarse\n"
" varios patrones\n"
" -h, --help muestra la ayuda y sale\n"
" --version muestra información de la versión y "
"sale\n"
"\n"
"gitinspector filtrará las estadísticas para incluir commits que modifiquen,\n"
"añaden o eliminen una de las extensiones especificadas, ver -f or "
"--file-types para\n"
"obtener más información\n"
"\n"
"gitinspector necesita que el ejecutable git este disponible en el PATH\n"
"Por favor, si encuentras errores en gitinspector ponte en contacto en\n"
"gitinspector@ejwa.se."
msgid ""
"WARNING: The terminal encoding is not correctly configured. gitinspector "
"might malfunction. The encoding can be configured with the environment "
"variable 'PYTHONIOENCODING'."
msgstr ""
"PRECAUCIÓN: La codificación del terminal no está configurada correctamente"
"gitinspector podría no funcionar de forma correcta. La codificación puede "
"configurarse con la variable `PYTHONIOENCODING`"
msgid "XML output not yet supported in"
msgstr "La salida en XML no esta aún soportada"
#, python-brace-format
msgid ""
"gitinspector requires at least Python 2.6 to run (version {0} was found)."
msgstr "Gitinspector requiere al menos Python 2.6 (se ha encontrado la versión"
" {0})"
msgid "invalid regular expression specified"
msgstr "La expresión regular especificada no es válida"
msgid "is mostly responsible for"
msgstr "es principalmente responsable de:"
msgid "specified output format not supported."
msgstr "el formato de salida especificado no está soportado"
#, python-brace-format
msgid "{0} ({1:.3f} in cyclomatic complexity density)"
msgstr "{0} En cyclomatic complexity density: {1:.3f})"
#, python-brace-format
msgid "{0} ({1} estimated lines of code)"
msgstr "{0} ({1} líneas de código estimadas)"
#, python-brace-format
msgid "{0} ({1} in cyclomatic complexity)"
msgstr "{0} (en cyclomatic complexity: {1})"

Binary file not shown.

View File

@ -0,0 +1,412 @@
# 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/>.
#
# Yannick Moy <yannick.moy@gmail.com>, 2014-2015
msgid ""
msgstr ""
"Project-Id-Version: gitinspector 0.5.0dev\n"
"Report-Msgid-Bugs-To: gitinspector@ejwa.se\n"
"POT-Creation-Date: 2015-10-02 03:35+0200\n"
"PO-Revision-Date: 2015-10-25 21:56+0100\n"
"Last-Translator: Yannick Moy <yannick.moy@gmail.com>\n"
"Language-Team: French <>\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#, python-format
msgid "% in comments"
msgstr "% en commentaires"
#, python-format
msgid "% of changes"
msgstr "% de modifications"
msgid "(extensions used during statistical analysis are marked)"
msgstr "(les extensions utilisées durant l'analyse statistique sont marquées)"
msgid "Age"
msgstr "Âge"
msgid "Author"
msgstr "Auteur"
msgid ""
"Below are the number of rows from each author that have survived and are "
"still intact in the current revision"
msgstr ""
"Le nombre de lignes par auteur encore présentes et intactes dans la version "
"courante est reporté ci-dessous"
#, python-brace-format
msgid "Checking how many rows belong to each author (Progress): {0:.0f}%"
msgstr ""
"Contrôle du nombre de lignes appartenant à chaque auteur (En progrès) : "
"{0:.0f}%"
msgid "Commits"
msgstr "Soumissions"
msgid ""
"Copyright © 2012-2015 Ejwa Software. All rights reserved.\n"
"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl."
"html>.\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n"
"\n"
"Written by Adam Waldenberg."
msgstr ""
"Copyright © 2012-2015 Ejwa Software. Tous droits réservés.\n"
"Licence GPLv3+: GNU GPL version 3 ou ultérieure <http://gnu.org/licenses/gpl."
"html>.\n"
"Le présent logiciel est un logiciel libre: you pouvez le modifier et le "
"redistributer librement.\n"
"Il n'y a AUCUNE GARANTIE, dans les limites autorisées par la loi.\n"
"\n"
"Écrit par Adam Waldenberg."
msgid "Deletions"
msgstr "Suppressions"
#, python-format
msgid "Error processing git repository at \"%s\"."
msgstr "Erreur pendant l'analyse du dépôt git"
msgid "HTML output not yet supported in"
msgstr "Sortie HTML pas encore prise en charge pour"
msgid "Hide minor authors"
msgstr "Masquer les auteurs mineurs"
msgid "Hide rows with minor work"
msgstr "Masquer les lignes avec le moins de travail"
msgid "Insertions"
msgstr "Insertions"
msgid "Minor Authors"
msgstr "Auteurs mineurs"
msgid "Modified Rows:"
msgstr "Lignes modifiées:"
msgid "No commited files with the specified extensions were found"
msgstr "Aucun fichier soumis avec l'extension spécifiée n'a été trouvé"
msgid "No metrics violations were found in the repository"
msgstr "Aucune violation des métriques n'a été constatée dans le dépôt"
#, python-brace-format
msgid "Repository statistics for {0}"
msgstr "Statistiques du dépôt {0}"
msgid "Rows"
msgstr "Lignes"
msgid "Show minor authors"
msgstr "Montrer les auteurs mineurs"
msgid "Show rows with minor work"
msgstr "Montrer les lignes avec le moins de travail"
msgid "Stability"
msgstr "Stabilité"
#, python-brace-format
msgid "Statistical information for the repository '{0}' was gathered on {1}."
msgstr ""
"Les informations statistiques pour le dépôt '{0}' ont été extraites le {1}."
msgid "Text output not yet supported in"
msgstr "Sortie textuelle pas encore prise en charge pour"
msgid ""
"The authors with the following emails were excluded from the statistics due "
"to the specified exclusion patterns"
msgstr ""
"Les auteurs avec les adresses e-mail suivantes ont été exclus des "
"statistiques en raison des filtres d'exclusion spécifiés"
msgid "The extensions below were found in the repository history"
msgstr "Les extensions suivantes ont été trouvées dans l'historique du dépôt"
msgid ""
"The following authors were excluded from the statistics due to the specified "
"exclusion patterns"
msgstr ""
"Les auteurs suivants ont été exclus des statistiques en raison des filtres "
"d'exclusion spécifiés"
msgid ""
"The following commit revisions were excluded from the statistics due to the "
"specified exclusion patterns"
msgstr ""
"Les soumissions suivantes ont été exclues des statistiques en raison des "
"filtres d'exclusion spécifiés"
msgid "The following files are suspiciously big (in order of severity)"
msgstr "Les fichiers suivants sont étrangement gros (par ordre d'importance)"
msgid ""
"The following files have an elevated cyclomatic complexity (in order of "
"severity)"
msgstr ""
"Les fichiers suivants ont une complexité cyclomatique étrangement élevée "
"(par ordre d'importance)"
msgid ""
"The following files have an elevated cyclomatic complexity density (in order "
"of severity)"
msgstr ""
"Les fichiers suivants ont une densité de complexité cyclomatique étrangement "
"élevée (par ordre d'importance)"
msgid ""
"The following files were excluded from the statistics due to the specified "
"exclusion patterns"
msgstr ""
"Les fichiers suivants ont été exclus des statistiques en raison des filtres "
"d'exclusion spécifiés"
msgid ""
"The following historical commit information, by author, was found in the "
"repository"
msgstr ""
"Les informations suivantes, par auteur, ont été extraites de l'historique de "
"soumission du dépôt"
msgid "The following history timeline has been gathered from the repository"
msgstr "La chronologie suivante a été extraite du dépôt"
msgid ""
"The following responsibilities, by author, were found in the current revision "
"of the repository (comments are excluded from the line count, if possible)"
msgstr ""
"Les responsabilités suivantes, par auteur, ont été extraites de la version "
"courante du dépôt (les commentaires sont exclus du compte des lignes, quand "
"c'est possible)"
msgid "The given option argument is not a valid boolean."
msgstr "L'option passée en argument n'est pas un booléen valide."
#, python-brace-format
msgid ""
"The output has been generated by {0} {1}. The statistical analysis tool for "
"git repositories."
msgstr ""
"Cette sortie a été générée par {0} {1}. L'outil d'analyse statistique pour "
"les dépôts git."
#, python-brace-format
msgid "Try `{0} --help' for more information."
msgstr "Entrez `{0} --help' pour plus d'information."
msgid "Unable to determine absolute path of git repository."
msgstr "Impossible de déterminer le chemin absolu du dépôt git."
#, fuzzy, python-brace-format
msgid ""
"Usage: {0} [OPTION]... [REPOSITORY]\n"
"List information about the repository in REPOSITORY. If no repository is\n"
"specified, the current directory is used. If multiple repositories are\n"
"given, information will be fetched from the last repository specified.\n"
"\n"
"Mandatory arguments to long options are mandatory for short options too.\n"
"Boolean arguments can only be given to long options.\n"
" -f, --file-types=EXTENSIONS a comma separated list of file extensions "
"to\n"
" include when computing statistics. The\n"
" default extensions used are:\n"
" {1}\n"
" Specifying * includes files with no\n"
" extension, while ** includes all files\n"
" -F, --format=FORMAT define in which format output should be\n"
" generated; the default format is 'text' "
"and\n"
" the available formats are:\n"
" {2}\n"
" --grading[=BOOL] show statistics and information in a way "
"that\n"
" is formatted for grading of student\n"
" projects; this is the same as supplying "
"the\n"
" options -HlmrTw\n"
" -H, --hard[=BOOL] track rows and look for duplicates harder;\n"
" this can be quite slow with big "
"repositories\n"
" -l, --list-file-types[=BOOL] list all the file extensions available in "
"the\n"
" current branch of the repository\n"
" -L, --localize-output[=BOOL] localize the generated output to the "
"selected\n"
" system language if a translation is\n"
" available\n"
" -m --metrics[=BOOL] include checks for certain metrics during "
"the\n"
" analysis of commits\n"
" -r --responsibilities[=BOOL] show which files the different authors "
"seem\n"
" most responsible for\n"
" --since=DATE only show statistics for commits more "
"recent\n"
" than a specific date\n"
" -T, --timeline[=BOOL] show commit timeline, including author "
"names\n"
" --until=DATE only show statistics for commits older than "
"a\n"
" specific date\n"
" -w, --weeks[=BOOL] show all statistical information in weeks\n"
" instead of in months\n"
" -x, --exclude=PATTERN an exclusion pattern describing the file\n"
" paths, revisions, revisions with certain\n"
" commit messages, author names or author\n"
" emails that should be excluded from the\n"
" statistics; can be specified multiple "
"times\n"
" -h, --help display this help and exit\n"
" --version output version information and exit\n"
"\n"
"gitinspector will filter statistics to only include commits that modify,\n"
"add or remove one of the specified extensions, see -f or --file-types for\n"
"more information.\n"
"\n"
"gitinspector requires that the git executable is available in your PATH.\n"
"Report gitinspector bugs to gitinspector@ejwa.se."
msgstr ""
"Utilisation: {0} [OPTION]... [RÉPERTOIRE]\n"
"Génère les informations sur le dépôt dans RÉPERTOIRE. Si aucun répertoire\n"
"n'est spécifié, le répertoire courant est utilisé. Si plusieurs répertoires\n"
"sont spécifiés, les informations seront données pour le dernier répertoire\n"
"spécifié.\n"
"\n"
"Les arguments obligatoires pour les options longues sont également "
"obligatoires\n"
"pour les options courtes.\n"
"Les argument booléens peuvent seulement être passés aux options longues.\n"
" -f, --file-types=EXTENSIONS liste séparée par des virgules des "
"extensions\n"
" de fichiers à considérer pour calculer "
"les\n"
" statistiques. Les extensions par défaut\n"
" sont:\n"
" {1}\n"
" Utiliser * pour inclure les fichiers\n"
" sans extension, et ** pour inclure tous\n"
" les fichiers\n"
" -F, --format=FORMAT définit le format de sortie à générer ;\n"
" le format par défaut est 'text' et\n"
" les formats disponibles sont:\n"
" {2}\n"
" --grading[=BOOL] montre les statistiques et les "
"informations\n"
" sous un format adapté pour noter des "
"projets\n"
" étudiants ; équivalent aux options -"
"HlmrTw\n"
" -H, --hard[=BOOL] suit les lignes et cherche les duplications "
"de\n"
" manière plus approfondie ; cela peut "
"être\n"
" assez long pour les dépôts très gros\n"
" -l, --list-file-types[=BOOL] énumère les extensions de fichiers "
"disponibles\n"
" dans la branche courante du dépôt\n"
" -L, --localize-output[=BOOL] traduit la sortie générée dans la langue\n"
" système sélectionnée, si une traduction "
"est\n"
" disponible\n"
" -m --metrics[=BOOL] inclut des contrôles pour certaines "
"métriques\n"
" durant l'analyse des soumissions\n"
" -r --responsibilities[=BOOL] montre de quels fichiers les différents "
"auteurs\n"
" semblent être responsables\n"
" --since=DATE montre les statistiques seulement pour les\n"
" soumissions plus récentes qu'une date\n"
" spécifiée\n"
" -T, --timeline[=BOOL] montre une chronologie des soumissions,\n"
" incluant le nom des auteurs\n"
" --until=DATE montre les statistiques seulement pour les\n"
" soumissions plus anciennes qu'une date\n"
" spécifiée\n"
" -w, --weeks[=BOOL] montre toutes les informations statistiques "
"en\n"
" semaines plutôt qu'en mois\n"
" -x, --exclude=PATTERN un filtre d'exclusion pour les noms de "
"fichiers,\n"
" soumissions, soumissions avec certains\n"
" messages de soumission, noms d'auteurs "
"ou\n"
" e-mails à exclure des statistiques ;\n"
" peut être spécifié plusieurs fois\n"
" -h, --help affiche ce message d'aide et quitte\n"
" le programme\n"
" --version affiche les informations de version et "
"quitte\n"
" le programme\n"
"\n"
"gitinspector filtre les statistiques pour n'inclure que les soumissions "
"qui \n"
"modifient, ajoutent ou retirent un fichier avec une des extensions "
"spécifiées, \n"
"voir -f ou --file-type pour plus d'information.\n"
"\n"
"gitinspector nécessite que l'exécutable git soit trouvé dans le PATH.\n"
"Signalez les bugs dans gitinspector à gitinspector@ejwa.se."
msgid ""
"WARNING: The terminal encoding is not correctly configured. gitinspector "
"might malfunction. The encoding can be configured with the environment "
"variable 'PYTHONIOENCODING'."
msgstr ""
"ATTENTION: L'encodage du terminal n'est pas configuré correctement. "
"gitinspector pourrait ne pas fonctionner correctement. L'encodage peut être "
"configuré avec la variable d'environnement 'PYTHONIOENCODING'."
msgid "XML output not yet supported in"
msgstr "Sortie XML pas encore prise en charge pour"
#, python-brace-format
msgid ""
"gitinspector requires at least Python 2.6 to run (version {0} was found)."
msgstr ""
"gitinspector nécessite Python 2.6 ou une version ultérieure (la version "
"trouvée est {0})."
msgid "invalid regular expression specified"
msgstr "l'expression régulière spécifiée est invalide"
msgid "is mostly responsible for"
msgstr "est principalement responsable de"
msgid "specified output format not supported."
msgstr "format de sortie spécifié non pris en charge."
#, python-brace-format
msgid "{0} ({1:.3f} in cyclomatic complexity density)"
msgstr "{0} ({1:.3f} en densité de complexité cyclomatique)"
#, python-brace-format
msgid "{0} ({1} estimated lines of code)"
msgstr "{0} ({1} lignes de code estimées)"
#, python-brace-format
msgid "{0} ({1} in cyclomatic complexity)"
msgstr "{0} ({1} en complexité cyclomatique)"

Binary file not shown.

View File

@ -0,0 +1,317 @@
# 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/>.
#
# Luca Motta <lucamot@gmail.com>, 2014-2015.
msgid ""
msgstr ""
"Project-Id-Version: gitinspector 0.5.0dev\n"
"Report-Msgid-Bugs-To: gitinspector@ejwa.se\n"
"POT-Creation-Date: 2015-10-02 03:35+0200\n"
"PO-Revision-Date: 2015-10-26 08:50+0100\n"
"Last-Translator: Luca Motta <lucamot@gmail.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language-Team: Italiano <>\n"
#, python-format
msgid "% in comments"
msgstr "% in commenti"
#, python-format
msgid "% of changes"
msgstr "% dei cambiamenti"
msgid "(extensions used during statistical analysis are marked)"
msgstr "(le estensioni usate nell'analisi statistica sono evidenziate)"
msgid "Age"
msgstr "Età"
msgid "Author"
msgstr "Autore"
msgid "Below are the number of rows from each author that have survived and are still intact in the current revision"
msgstr "Di seguito il numero di righe da ogni autore che sono sopravvissute a sono ancora intatte nella versione corrente"
#, python-brace-format
msgid "Checking how many rows belong to each author (Progress): {0:.0f}%"
msgstr "Controllo quante righe appartengono ad ogni autore (Completamento): {0:.0f}%"
msgid "Commits"
msgstr "Commit"
msgid ""
"Copyright © 2012-2015 Ejwa Software. All rights reserved.\n"
"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n"
"\n"
"Written by Adam Waldenberg."
msgstr ""
"Copyright © 2012-2015 Ejwa Software. Tutti i diritti riservati.\n"
"Licenza GPLv3+: GNU GPL versione 3 or successive <http://gnu.org/licenses/gpl.html>.\n"
"Questo software è gratuito: siete liberi di cambiarlo e ridistribuirlo.\n"
"NON CI SONO GARANZIE, nei limiti permessi dalla legge.\n"
"\n"
"Scritto by Adam Waldenberg."
msgid "Deletions"
msgstr "Rimozioni"
#, python-format
msgid "Error processing git repository at \"%s\"."
msgstr "Errore nel processamento del repository GIT \"%s\"."
msgid "HTML output not yet supported in"
msgstr "Output HTML non ancora supportato"
msgid "Hide minor authors"
msgstr "Nascondi autori minori"
msgid "Hide rows with minor work"
msgstr "Nascondi righe con lavoro secondario"
msgid "Insertions"
msgstr "Inserimenti"
msgid "Minor Authors"
msgstr "Autori secondari"
msgid "Modified Rows:"
msgstr "Righe modificate:"
msgid "No commited files with the specified extensions were found"
msgstr "Non sono stati trovati file committati con l'estensione specificata"
msgid "No metrics violations were found in the repository"
msgstr "Non sono state rilevate violazioni delle metriche nel repository"
#, python-brace-format
msgid "Repository statistics for {0}"
msgstr "Statistiche del repository per {0}"
msgid "Rows"
msgstr "Righe"
msgid "Show minor authors"
msgstr "Visualizza autori secondari"
msgid "Show rows with minor work"
msgstr "Visualizza righe con lavoro secondario"
msgid "Stability"
msgstr "Stabilità"
#, python-brace-format
msgid "Statistical information for the repository '{0}' was gathered on {1}."
msgstr "Informazioni statistiche per il repository '{0}' raccolte in {1}."
msgid "Text output not yet supported in"
msgstr "Output testuale non ancora supportato"
msgid "The authors with the following emails were excluded from the statistics due to the specified exclusion patterns"
msgstr "Gli autori con le seguenti email sono stati esclusi dalle statistiche come da pattern di esclusione"
msgid "The extensions below were found in the repository history"
msgstr "Le seguenti estensioni sono state trovate nell'history del repository"
msgid "The following authors were excluded from the statistics due to the specified exclusion patterns"
msgstr "I seguenti autori sono stati esclusi dalle statistiche come da pattern di esclusione"
msgid "The following commit revisions were excluded from the statistics due to the specified exclusion patterns"
msgstr "Le revision seguenti sono state escluse dalle statistiche come da pattern di esclusione"
msgid "The following files are suspiciously big (in order of severity)"
msgstr "I seguenti file sono stranamente grandi (in ordine di importanza)"
msgid "The following files have an elevated cyclomatic complexity (in order of severity)"
msgstr "I seguenti file hanno un'elevata complessità ciclomatica (in ordine di importanza)"
msgid "The following files have an elevated cyclomatic complexity density (in order of severity)"
msgstr "I seguenti file hanno un'elevata densità ciclomatica (in ordine di importanza)"
msgid "The following files were excluded from the statistics due to the specified exclusion patterns"
msgstr "I seguenti file sono stati esclusi dalle statistiche come da pattern di esclusione"
msgid "The following historical commit information, by author, was found in the repository"
msgstr "Le seguenti informazioni storiche sui commit, per autore, sono state trovate nel repository"
msgid "The following history timeline has been gathered from the repository"
msgstr "La seguente timeline storica è stata ricavata dal repository"
msgid "The following responsibilities, by author, were found in the current revision of the repository (comments are excluded from the line count, if possible)"
msgstr "Le seguenti responsabilità, per autore, sono state trovate nella revision corrente del repository (i commenti sono esclusi dal conteggio delle linee, se possibile)"
msgid "The given option argument is not a valid boolean."
msgstr "L'argomento opzionale passato non è un valore booleano valido."
#, python-brace-format
msgid "The output has been generated by {0} {1}. The statistical analysis tool for git repositories."
msgstr "L'output è stato generato da {0} {1}. Il tool di analisi statistica per repository GIT."
#, python-brace-format
msgid "Try `{0} --help' for more information."
msgstr "`{0} --help' per maggiori informazioni."
msgid "Unable to determine absolute path of git repository."
msgstr "Impossibile determinare un path assoluto di un repository GIT."
#, python-brace-format
msgid ""
"Usage: {0} [OPTION]... [REPOSITORY]\n"
"List information about the repository in REPOSITORY. If no repository is\n"
"specified, the current directory is used. If multiple repositories are\n"
"given, information will be fetched from the last repository specified.\n"
"\n"
"Mandatory arguments to long options are mandatory for short options too.\n"
"Boolean arguments can only be given to long options.\n"
" -f, --file-types=EXTENSIONS a comma separated list of file extensions to\n"
" include when computing statistics. The\n"
" default extensions used are:\n"
" {1}\n"
" Specifying * includes files with no\n"
" extension, while ** includes all files\n"
" -F, --format=FORMAT define in which format output should be\n"
" generated; the default format is 'text' and\n"
" the available formats are:\n"
" {2}\n"
" --grading[=BOOL] show statistics and information in a way that\n"
" is formatted for grading of student\n"
" projects; this is the same as supplying the\n"
" options -HlmrTw\n"
" -H, --hard[=BOOL] track rows and look for duplicates harder;\n"
" this can be quite slow with big repositories\n"
" -l, --list-file-types[=BOOL] list all the file extensions available in the\n"
" current branch of the repository\n"
" -L, --localize-output[=BOOL] localize the generated output to the selected\n"
" system language if a translation is\n"
" available\n"
" -m --metrics[=BOOL] include checks for certain metrics during the\n"
" analysis of commits\n"
" -r --responsibilities[=BOOL] show which files the different authors seem\n"
" most responsible for\n"
" --since=DATE only show statistics for commits more recent\n"
" than a specific date\n"
" -T, --timeline[=BOOL] show commit timeline, including author names\n"
" --until=DATE only show statistics for commits older than a\n"
" specific date\n"
" -w, --weeks[=BOOL] show all statistical information in weeks\n"
" instead of in months\n"
" -x, --exclude=PATTERN an exclusion pattern describing the file\n"
" paths, revisions, revisions with certain\n"
" commit messages, author names or author\n"
" emails that should be excluded from the\n"
" statistics; can be specified multiple times\n"
" -h, --help display this help and exit\n"
" --version output version information and exit\n"
"\n"
"gitinspector will filter statistics to only include commits that modify,\n"
"add or remove one of the specified extensions, see -f or --file-types for\n"
"more information.\n"
"\n"
"gitinspector requires that the git executable is available in your PATH.\n"
"Report gitinspector bugs to gitinspector@ejwa.se."
msgstr ""
"Uso: {0} [OPZIONE]... [REPOSITORY]\n"
"Elenca le informazioni sul repository in REPOSITORY. Se non si specifica\n"
"un repository, usa la directory corrente. Se sono passati più repository,\n"
"le informazioni saranno raccolte dall'ultimo dei repository specificati.\n"
"\n"
"Gli argomenti obbligatori per le opzioni in formato esteso sono obbligatori\n"
"anche per le opzioni in formato ridotto.\n"
"Gli argomenti booleani possono essere passati solo alle opzioni estese.\n"
" -f, --file-types=EXTENSIONS lista separate da virgole di estensioni da\n"
" includere quando si calcolano le\n"
" statistiche. Le estensioni di default\n"
" usate sono:\n"
" {1}\n"
" Specificando * si includono i file senza\n"
" estensione mentre ** include tutti i file\n"
" -F, --format=FORMAT definisce in che formato deve essere l'output;\n"
" il formato di default è 'text' e quelli\n"
" disponibili sono:\n"
" {2}\n"
" --grading[=BOOL] visualizza le informazionie e le statistiche\n"
" formattate per i progetti da studente; è\n"
" equivalente alle opzioni -HlmrTw\n"
" -H, --hard[=BOOL] traccia le righe e cerca i duplicati in modo\n"
" più approfondito; può essere molto lento\n"
" per repository grandi\n"
" -l, --list-file-types[=BOOL] elenca le estensioni disponibili nel branch\n"
" corrente del repository\n"
" -L, --localize-output[=BOOL] localizza l'output generato nella lingua\n"
" selezionata, se è disponibile\n"
" -m --metrics[=BOOL] include controlli per alcune metriche durante\n"
" l'analisi dei commit\n"
" -r --responsibilities[=BOOL] visualizza quali autori sembrano essere più\n"
" responsabili di determinati file\n"
" --since=DATE visualizza solo le statistiche per i commit\n"
" più recenti di una data specifica\n"
" -T, --timeline[=BOOL] visualizza la timeline dei commit, inclusi i\n"
" nomi degli autori\n"
" --until=DATE visualizza solo le statistiche per i commit\n"
" più vecchi di una data specifica\n"
" -w, --weeks[=BOOL] visualizza tutte le informazioni statistiche\n"
" per settimane anziché per mesi\n"
" -x, --exclude=PATTERN pattern di esclusione che descrivono percorsi\n"
" di file, revision, revision con determinati\n"
" messaggi, nomi di autori o email di autori\n"
" che devono essere esclusi dalle statistiche;\n"
" può essere specificato più di una volta\n"
" -h, --help visualizza questo messaggio ed esce\n"
" --version visualizza le informazioni sulla versione ed\n"
" esce\n"
"\n"
"gitinspector filtrerà le statistiche per includere solo commit che modificano,\n"
"aggiungono o rimuovono una delle estensioni specificate, vedere -f o\n"
"--file-types per altre informazioni.\n"
"\n"
"gitinspector richiede che l'eseguibile GIT sia nel PATH corrente.\n"
"Segnala i bug di gitinspector a gitinspector@ejwa.se."
msgid "WARNING: The terminal encoding is not correctly configured. gitinspector might malfunction. The encoding can be configured with the environment variable 'PYTHONIOENCODING'."
msgstr "ATTENZIONE: L'encoding del terminale non è configurato correttamente. gitinspector potrebbe non funzionare correttamente. L'encoding può essere configurato con la variabile d'ambiente 'PYTHONIOENCODING'"
msgid "XML output not yet supported in"
msgstr "Output XML non ancora supportato"
#, python-brace-format
msgid "gitinspector requires at least Python 2.6 to run (version {0} was found)."
msgstr "gitinspector richiede almeno Python 2.6 per essere eseguito (è stata trovata la versione {0})."
msgid "invalid regular expression specified"
msgstr "l'espressione regolare specificata non è valida"
msgid "is mostly responsible for"
msgstr "è responsabile per"
msgid "specified output format not supported."
msgstr "il formato di output specificato non è supportato."
#, python-brace-format
msgid "{0} ({1:.3f} in cyclomatic complexity density)"
msgstr "{0} ({1:.3f} in densità di complessità ciclomatica)"
#, python-brace-format
msgid "{0} ({1} estimated lines of code)"
msgstr "{0} ({1} linee di codice stimate)"
#, python-brace-format
msgid "{0} ({1} in cyclomatic complexity)"
msgstr "{0} ({1} in complessità ciclomatica)"

Binary file not shown.

View File

@ -0,0 +1,313 @@
# 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/>.
#
# Kamila Chyla <kamila.chyla@gmail.com>, 2015.
msgid ""
msgstr ""
"Project-Id-Version: gitinspector 0.5.0dev\n"
"Report-Msgid-Bugs-To: gitinspector@ejwa.se\n"
"POT-Creation-Date: 2015-10-02 03:35+0200\n"
"PO-Revision-Date: 2015-10-24 07:50+0100\n"
"Last-Translator: Kamila Chyla <kamila.chyla@gmail.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language-Team: Polish <>\n"
#, python-format
msgid "% in comments"
msgstr "% w komentarzach"
#, python-format
msgid "% of changes"
msgstr "% zmian"
msgid "(extensions used during statistical analysis are marked)"
msgstr "(zaznaczono rozszerzenia użyte podczas analizy statystycznej)"
msgid "Age"
msgstr "Wiek"
msgid "Author"
msgstr "Autor"
msgid "Below are the number of rows from each author that have survived and are still intact in the current revision"
msgstr "Poniżej podano liczbę wierszy przypadających na autora, które w niezmienionej postaci przetrwały do obecnego stanu rezpozytorium"
#, python-brace-format
msgid "Checking how many rows belong to each author (Progress): {0:.0f}%"
msgstr "Sprawdzam ile wierszy należy do każdego z autorów (Postęp): {0:.0f}%"
msgid "Commits"
msgstr "Komity"
msgid ""
"Copyright © 2012-2015 Ejwa Software. All rights reserved.\n"
"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n"
"\n"
"Written by Adam Waldenberg."
msgstr ""
"Copyright © 2012-2015 Ejwa Software. Wszystkie prawa zastrzeżone.\n"
"Licencja GPLv3+: GNU GPL wersja 3 lub późniejsza <http://gnu.org/licenses/gpl.html>.\n"
"To jest wolne oprogramowanie: możesz je bezpłatnie zmieniać i dystrybuować.\n"
"W zakresie określonym prawem, NIE MA GWARANCJI.\n"
"\n"
"Napisane przez Adama Waldenberga."
msgid "Deletions"
msgstr "Usunięcia"
#, python-format
msgid "Error processing git repository at \"%s\"."
msgstr "Błąd podczas przetwarzania repozytorium w \"%s\"."
msgid "HTML output not yet supported in"
msgstr "Wyjście w formacie HTML nie jest jeszcze wspierane w"
msgid "Hide minor authors"
msgstr "Ukryj mniej znaczących autorów"
msgid "Hide rows with minor work"
msgstr "Ukryj wiersze z mniej znaczącymi zmianami"
msgid "Insertions"
msgstr "Wstawienia"
msgid "Minor Authors"
msgstr "Mniej znaczący autorzy"
msgid "Modified Rows:"
msgstr "Zmienione wiersze:"
msgid "No commited files with the specified extensions were found"
msgstr "Brak wkomitowanych plików o podanych rozszerzeniach."
msgid "No metrics violations were found in the repository"
msgstr "W repozytorium nie stwierdzono naruszenia metryk."
#, python-brace-format
msgid "Repository statistics for {0}"
msgstr "Statystyki repozytorium dla {0}"
msgid "Rows"
msgstr "Wiersze"
msgid "Show minor authors"
msgstr "Pokaż mniej znaczących autorów"
msgid "Show rows with minor work"
msgstr "Pokaż wiersze z mniej znaczącymi zmianami"
msgid "Stability"
msgstr "Stabilność"
#, python-brace-format
msgid "Statistical information for the repository '{0}' was gathered on {1}."
msgstr "Informacje statystyczne dla repozytorium '{0}' zostały zebrane {1}"
msgid "Text output not yet supported in"
msgstr "Wyjście tekstowe nie jest jeszcze wspierane w"
msgid "The authors with the following emails were excluded from the statistics due to the specified exclusion patterns"
msgstr "Autorzy z następującymi adresami e-mail zostali wyłączeni ze statystyk zgodnie z określonymi wzorcami wykluczającymi"
msgid "The extensions below were found in the repository history"
msgstr "W historii repozytorium znajdują się poniższe rozszerzenia plików."
msgid "The following authors were excluded from the statistics due to the specified exclusion patterns"
msgstr "Następujący autorzy zostali wyłączeni ze statystyk z powodu użycia wzorców wykluczających"
msgid "The following commit revisions were excluded from the statistics due to the specified exclusion patterns"
msgstr "Następujące komity zostały wyłączone ze statystyk z powodu użycia wzorców wykluczających"
msgid "The following files are suspiciously big (in order of severity)"
msgstr "Następujące pliki są podejrzanie duże (od największych)"
msgid "The following files have an elevated cyclomatic complexity (in order of severity)"
msgstr "Następujące pliki mają zwiększoną złożoność cyklometryczną (od największych)"
msgid "The following files have an elevated cyclomatic complexity density (in order of severity)"
msgstr "Następujące pliki mają zwiększoną gęstość złożoności cyklometrycznej (od największych)"
msgid "The following files were excluded from the statistics due to the specified exclusion patterns"
msgstr "Ze względu na podane wykluczające wzorce wyrażeń regularnych następujące pliki zostały w statystykach pominięte."
msgid "The following historical commit information, by author, was found in the repository"
msgstr "Informacje o wykonanych komitach, uporządkowane według autorów"
msgid "The following history timeline has been gathered from the repository"
msgstr "Z repozytorium wygenerowano następującą historię zmian"
msgid "The following responsibilities, by author, were found in the current revision of the repository (comments are excluded from the line count, if possible)"
msgstr "W bieżącej wersji repozytorium zidentyfikowano następujące odpowiedzialności, uporządkowane według autorów (nie uwzględniono, o ile było to możliwe, wierszy zawierających komentarze."
msgid "The given option argument is not a valid boolean."
msgstr "Podany argument opcji nie jest poprawną wartością logiczną."
#, python-brace-format
msgid "The output has been generated by {0} {1}. The statistical analysis tool for git repositories."
msgstr "Dane zostały wygenerowane przez {0} {1}. Narzędzie analizy statystycznej dla repozytoriów git."
#, python-brace-format
msgid "Try `{0} --help' for more information."
msgstr "Aby dowiedzieć się więcej, wpisz `{0} --help'"
msgid "Unable to determine absolute path of git repository."
msgstr "Nie można określić ścieżki bezwzględnej do repozytorium git-a"
#, python-brace-format
msgid ""
"Usage: {0} [OPTION]... [REPOSITORY]\n"
"List information about the repository in REPOSITORY. If no repository is\n"
"specified, the current directory is used. If multiple repositories are\n"
"given, information will be fetched from the last repository specified.\n"
"\n"
"Mandatory arguments to long options are mandatory for short options too.\n"
"Boolean arguments can only be given to long options.\n"
" -f, --file-types=EXTENSIONS a comma separated list of file extensions to\n"
" include when computing statistics. The\n"
" default extensions used are:\n"
" {1}\n"
" -F, --format=FORMAT define in which format output should be\n"
" generated; the default format is 'text' and\n"
" the available formats are:\n"
" {2}\n"
" --grading[=BOOL] show statistics and information in a way that\n"
" is formatted for grading of student\n"
" projects; this is the same as supplying the\n"
" options -HlmrTw\n"
" -H, --hard[=BOOL] track rows and look for duplicates harder;\n"
" this can be quite slow with big repositories\n"
" -l, --list-file-types[=BOOL] list all the file extensions available in the\n"
" current branch of the repository\n"
" -L, --localize-output[=BOOL] localize the generated output to the selected\n"
" system language if a translation is\n"
" available\n"
" -m --metrics[=BOOL] include checks for certain metrics during the\n"
" analysis of commits\n"
" -r --responsibilities[=BOOL] show which files the different authors seem\n"
" most responsible for\n"
" --since=DATE only show statistics for commits more recent\n"
" than a specific date\n"
" -T, --timeline[=BOOL] show commit timeline, including author names\n"
" --until=DATE only show statistics for commits older than a\n"
" specific date\n"
" -w, --weeks[=BOOL] show all statistical information in weeks\n"
" instead of in months\n"
" -x, --exclude=PATTERN an exclusion pattern describing the file\n"
" paths, revisions, author names or author\n"
" emails that should be excluded from the\n"
" statistics; can be specified multiple times\n"
" -h, --help display this help and exit\n"
" --version output version information and exit\n"
"\n"
"gitinspector will filter statistics to only include commits that modify,\n"
"add or remove one of the specified extensions, see -f or --file-types for\n"
"more information.\n"
"\n"
"gitinspector requires that the git executable is available in your PATH.\n"
"Report gitinspector bugs to gitinspector@ejwa.se."
msgstr ""
"Użycie: {0} [OPCJA]... [REPOZYTORIUM]\n"
"Wypisuje informacje o repozytorium znajdującym się w repozytorium REPOZYTORIUM.\n"
"Jeśli nie podano repozytorium, użyte jest bieżące. Jeśli podano więcej niż jedno,\n"
"informacje zostaną wygenerowane na podstawie ostatniego z nich.\n"
"\n"
"Obowiązkowe argumenty długich opcji są obowiązkowe również dla opcji krótkich.\n"
"Argumenty typu Boolean mogą być użyte wyłącznie w opcjach długich.\n"
" -f, --file-types=EXTENSIONS lista rozdzielonych przecinkami rozszerzeń\n"
" uwzględnionych przy liczeniu statystyk; \n"
" domyślne rozszerzenia to:\n"
" {1}\n"
" Przekazanie * uwzględnia pliki bez rozszerzeń,\n"
" natomiast ** uwzględnia wszystkie pliki.\n"
" -F, --format=FORMAT definiuje format wyściowy raportu;\n"
" domyślnym formatem jest 'text';\n"
" dostępne formaty to:\n"
" {2}\n"
" --grading[=BOOL] pokazuje statystyki i informacje sformatowane\n"
" w sposób ułatwiający ocenianie projektów\n"
" studenckich; odpowiada wywołaniu z opcjami\n"
" -HlmrTw\n"
" -H, --hard[=BOOL] dokładniej analizuje wiersze kodu i szuka\n"
" powtórzeń; może długo działać na dużych\n"
" repozytoriach\n"
" -l, --list-file-types[=BOOL] wyświetla wszystkie rozszerzenia używane w\n"
" bieżącej gałęzi repozytorium\n"
" -L, --localize-output[=BOOL] jeśli dostępne jest tłumaczenie na język\n"
" określony w locale systemu, używa go przy\n"
" generowaniu wyjścia\n"
" -m --metrics[=BOOL] w czasie analizowania komitów używa metryk\n"
" -r --responsibilities[=BOOL] pokazuje pliki za które ich autorzy wydają się\n"
" być najbardziej odpowiedzialni\n"
" --since=DATE pokazuje statystyki dla komitów powstałych po\n"
" określonej dacie\n"
" -T, --timeline[=BOOL] pokazuje zmiany w czasie wraz z nazwiskami\n"
" autorów\n"
" --until=DATE pokazuje statystyki dla komitów powstałych\n"
" prze dokreśloną datą\n"
" -w, --weeks[=BOOL] pokazuje informacje statystyczne w tygodniach\n"
" zamiast w miesiącach\n"
" -x, --exclude=PATTERN używa wzorca wykluczającego z analizy\n"
" statystycznej nazwy plików, komity, komity"
" zawierające określoną treść, imiona i\n"
" nazwiska, adresy e-mail; opcja może być\n"
" użyta wielokrotnie\n"
" -h, --help wyświetla tę informację i kończy działanie\n"
" --version wyświetla informację o wersji i kończy\n"
" działanie\n"
"\n"
"gitinspector uwzględni w statystyce wyłącznie te komity, które zmieniają,\n"
"dodają lub usuwają pliki posiadające jedno z określonych rozszerzeń;\n"
"aby uzyskać więcej informacji, sprawdź opcję -f lub --file-types.\n"
"\n"
"gitinspector wymaga, aby program wykonywalny git był dostępny w zmiennej PATH.\n"
"Błędy w programie gitinspector należy zgłaszać na adres gitinspector@ejwa.se."
msgid "WARNING: The terminal encoding is not correctly configured. gitinspector might malfunction. The encoding can be configured with the environment variable 'PYTHONIOENCODING'."
msgstr "OSTRZEŻENIE: Kodowanie używane przez terminal jest niepoprawnie skonfigurowane. gitinspector może niepoprawnie działać. Kodowanie można określić ustawiając zmienną środowiskową 'PYTHONIOENCODING'."
msgid "XML output not yet supported in"
msgstr "Wyjście w formacie XML nie jest jeszcze wspierane w"
#, python-brace-format
msgid "gitinspector requires at least Python 2.6 to run (version {0} was found)."
msgstr "gitinspector wymaga wersji Pythona co najmniej 2.6 (znaleziono wersję {0})."
msgid "invalid regular expression specified"
msgstr "podano niepoprawne wyrażenie regularne"
msgid "is mostly responsible for"
msgstr "jest główną osobą odpowiedzialną za"
msgid "specified output format not supported."
msgstr "podany format wyjściowy nie jest obsługiwany."
#, python-brace-format
msgid "{0} ({1:.3f} in cyclomatic complexity density)"
msgstr "{0} ({1:.3f} w gęstości złożoności cyklometrycznej)"
#, python-brace-format
msgid "{0} ({1} estimated lines of code)"
msgstr "{0} (szacunkowo {1} wierszy kodu)"
#, python-brace-format
msgid "{0} ({1} in cyclomatic complexity)"
msgstr "{0} ({1} w złożoności cyklometrycznej)"

View File

@ -1,4 +1,4 @@
# Copyright © 2012-2013 Ejwa Software. All rights reserved.
# Copyright © 2012-2015 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -15,145 +15,38 @@
# You should have received a copy of the GNU General Public License
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
#
# Adam Waldenberg <adam.waldenberg@ejwa.se>, 2013.
# Adam Waldenberg <adam.waldenberg@ejwa.se>, 2013-2015.
msgid ""
msgstr ""
"Project-Id-Version: gitinspector 0.3.1\n"
"Project-Id-Version: gitinspector 0.5.0dev\n"
"Report-Msgid-Bugs-To: gitinspector@ejwa.se\n"
"POT-Creation-Date: 2013-07-15 02:57+0200\n"
"PO-Revision-Date: 2013-07-15 03:08+0200\n"
"POT-Creation-Date: 2015-10-02 03:35+0200\n"
"PO-Revision-Date: 2015-10-02 04:12+0200\n"
"Last-Translator: Adam Waldenberg <adam.waldenberg@ejwa.se>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language-Team: Swedish <>\n"
"Language-Team: Svenska <>\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Gtranslator 2.91.6\n"
msgid "The extensions below were found in the repository history"
msgstr "Suffixen nedan hittades i förrådshistoriken"
msgid "(extensions used during statistical analysis are marked)"
msgstr "(suffix som användes vid den statistiska analysen är markerade)"
msgid ""
"The following historical commit information, by author, was found in the "
"repository"
msgstr ""
"Den följande inlämningshistoriken, efter upphovsman, hittades i förrådet"
msgid "No commited files with the specified extensions were found"
msgstr "Kunde inte hitta några inlämnade filer med det angivna suffixet"
msgid "Author"
msgstr "Upphovsman"
msgid "Commits"
msgstr "Inlämningar"
msgid "Insertions"
msgstr "Insättningar"
msgid "Deletions"
msgstr "Borttagningar"
#, python-format
msgid "% in comments"
msgstr "% i kommentarer"
#, python-format
msgid "% of changes"
msgstr "% av ändringar"
msgid "Minor Authors"
msgstr "Mindre Upphovsmän"
msgid "(extensions used during statistical analysis are marked)"
msgstr "(suffix som användes vid den statistiska analysen är markerade)"
msgid "specified output format not supported."
msgstr "det angivna utmatningsformatet stöds inte."
msgid "Age"
msgstr "Ålder"
msgid ""
"The following repsonsibilties, by author, were found in the current revision "
"of the repository (comments are exluded from the line count, if possible)"
msgstr ""
"Följande ansvar, utefter upphovsman, hittades i den nuvarande revisionen av "
"förrådet (kommentarer är uteslutna från radberäkningen, om så möjligt)"
msgid "is mostly responsible for"
msgstr "är mestandels ansvarig för"
msgid ""
"Copyright © 2012-2013 Ejwa Software. All rights reserved.\n"
"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl."
"html>.\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n"
"\n"
"Written by Adam Waldenberg."
msgstr ""
"Upphovsrätt © 2012-2013 Ejwa Software. Alla rättigheter förbehållna.\n"
"Licens GPLv3+: GNU GPL version 3 eller senare <http://gnu.org/licenses/gpl."
"html>.\n"
"Detta är fri programvara: du får lov att ändra och vidaredistribuera den.\n"
"Det finns INGEN GARANTI, så långt lagen tillåter.\n"
"\n"
"Skrivet av Adam Waldenberg."
msgid ""
"gitinspector requires at least Python 2.6 to run (version {0} was found)."
msgstr "gitinspector kräver åtminstone Python 2.6 (version {0} hittades)."
msgid "Try `{0} --help' for more information."
msgstr "Försök med `{0} --help' för mer information."
msgid ""
"The output has been generated by {0}; the statistical analysis tool for git "
"repositories."
msgstr ""
"Sidan har skapats av {0}; det statistiska analysverktyget för git-förråd."
msgid "Show minor authors"
msgstr "Visa mindre upphovsmän"
msgid "Hide minor authors"
msgstr "Göm mindre upphovsmän"
msgid "Show rows with minor work"
msgstr "Visa rader med lite arbete"
msgid "Hide rows with minor work"
msgstr "Göm rader med lite arbete"
msgid "HTML output not yet supported in"
msgstr "HTML-utmatning stöds inte i"
msgid "Text output not yet supported in"
msgstr "Textutmatning stöds inte i"
msgid "XML output not yet supported in"
msgstr "XML-utmatning stöds inte i"
msgid ""
"The following files were missing in the repository and were therefore not "
"completely included in the statistical analysis. To include them, you can "
"either checkout manually using git or use the -c option in gitinspector"
msgstr ""
"Följande filer saknades i förrådet och var därför inte helt inräknade i den "
"statistiska analysen. För att inkludera dem så kan du antingen checka ut dem "
"manuellt med git eller använda -c flaggan i gitinspector"
msgid "The following files are suspiciously big (in order of severity)"
msgstr ""
"Följande filer är misstänksamt stora (sorterat utefter allvarlighetsgrad)"
msgid "No metrics violations were found in the repository"
msgstr "Inga överträdelser av kodmetrik hittades i förrådet"
msgid "The following history timeline has been gathered from the repository"
msgstr "Den följande historiska tidslinjen har samlats in från förrådet"
msgid "Modified Rows:"
msgstr "Ändrade Rader:"
msgid "Checking how many rows belong to each author (Progress): {0:.0f}%"
msgstr ""
"Kontrollerar hur många rader som tillhör varje författare (Framsteg): "
"{0:.0f}%"
msgid "Author"
msgstr "Upphovsman"
msgid ""
"Below are the number of rows from each author that have survived and are "
@ -162,81 +55,214 @@ msgstr ""
"Nedan återges antalet rader från varje upphovsman som har överlevt och "
"fortfarande är intakta i den nuvarande revisionen"
#, python-brace-format
msgid "Checking how many rows belong to each author (Progress): {0:.0f}%"
msgstr ""
"Kontrollerar hur många rader som tillhör varje författare (Framsteg): "
"{0:.0f}%"
msgid "Commits"
msgstr "Inlämningar"
msgid ""
"Copyright © 2012-2015 Ejwa Software. All rights reserved.\n"
"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl."
"html>.\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n"
"\n"
"Written by Adam Waldenberg."
msgstr ""
"Upphovsrätt © 2012-2015 Ejwa Software. Alla rättigheter förbehållna.\n"
"Licens GPLv3+: GNU GPL version 3 eller senare <http://gnu.org/licenses/gpl."
"html>.\n"
"Detta är fri programvara: du får lov att ändra och vidaredistribuera den.\n"
"Det finns INGEN GARANTI, så långt lagen tillåter.\n"
"\n"
"Skrivet av Adam Waldenberg."
msgid "Deletions"
msgstr "Borttagningar"
#, python-format
msgid "Error processing git repository at \"%s\"."
msgstr "Kunde inte hitta git-förråd på \"%s\"."
msgid "HTML output not yet supported in"
msgstr "HTML-utmatning stöds inte i"
msgid "Hide minor authors"
msgstr "Göm mindre upphovsmän"
msgid "Hide rows with minor work"
msgstr "Göm rader med lite arbete"
msgid "Insertions"
msgstr "Insättningar"
msgid "Minor Authors"
msgstr "Mindre Upphovsmän"
msgid "Modified Rows:"
msgstr "Ändrade Rader:"
msgid "No commited files with the specified extensions were found"
msgstr "Kunde inte hitta några inlämnade filer med det angivna suffixet"
msgid "No metrics violations were found in the repository"
msgstr "Inga överträdelser av kodmetrik hittades i förrådet"
#, python-brace-format
msgid "Repository statistics for {0}"
msgstr "Statistik för förrådet {0}"
msgid "Rows"
msgstr "Rader"
#, python-format
msgid "% in comments"
msgstr "% i kommentarer"
msgid "Show minor authors"
msgstr "Visa mindre upphovsmän"
msgid "The given option argument is not a valid boolean."
msgstr "Det angivna flaggargumentet är inte en giltig boolean."
msgid "Show rows with minor work"
msgstr "Visa rader med lite arbete"
msgid "invalid regular expression specified"
msgstr "ogiltigt reguljärt uttryck angivet"
msgid "Stability"
msgstr "Stabilitet"
#, python-brace-format
msgid "Statistical information for the repository '{0}' was gathered on {1}."
msgstr "Statistikinformation för förrådet '{0}' hämtades {1}."
msgid "Text output not yet supported in"
msgstr "Textutmatning stöds inte i"
msgid ""
"The authors with the following emails were excluded from the statistics due "
"to the specified exclusion patterns"
msgstr ""
"Upphovsmännen med följande emailadresser har uteslutits från den statistiska "
"analysen till följd av de angivna uteslutningsreglerna"
msgid "The extensions below were found in the repository history"
msgstr "Suffixen nedan hittades i förrådshistoriken"
msgid ""
"The following authors were excluded from the statistics due to the specified "
"exclusion patterns"
msgstr ""
"Följande upphovsmän har uteslutits från den statistiska analysen till följd "
"av de angivna uteslutningsreglerna"
msgid ""
"The following commit revisions were excluded from the statistics due to the "
"specified exclusion patterns"
msgstr ""
"Följande inlämningsrevisioner har uteslutits från den statistiska analysen "
"till följd av de angivna uteslutningsreglerna"
msgid "The following files are suspiciously big (in order of severity)"
msgstr ""
"Följande filer är misstänksamt stora (sorterat utefter allvarlighetsgrad)"
msgid ""
"The following files have an elevated cyclomatic complexity (in order of "
"severity)"
msgstr ""
"Följande filer har en förhöjd cyklomatisk komplexitet (sorterat utefter "
"allvarlighetsgrad)"
msgid ""
"The following files have an elevated cyclomatic complexity density (in order "
"of severity)"
msgstr ""
"Följande filer har en förhöjd cyklomatisk komplexitetsdensitet (sorterat "
"utefter allvarlighetsgrad)"
msgid ""
"The following files were excluded from the statistics due to the specified "
"exclusion patterns"
msgstr ""
"Följande filer var uteslutna från den statistiska analysen på grund av de "
"Följande filer har uteslutits från den statistiska analysen till följd av de "
"angivna uteslutningsreglerna"
msgid ""
"Usage: {0} [OPTION]... [DIRECTORY]\n"
"List information about the repository in DIRECTORY. If no directory is\n"
"specified, the current directory is used. If multiple directories are\n"
"given, information will be fetched from the last directory specified.\n"
"The following historical commit information, by author, was found in the "
"repository"
msgstr ""
"Den följande inlämningshistoriken, efter upphovsman, hittades i förrådet"
msgid "The following history timeline has been gathered from the repository"
msgstr "Den följande historiska tidslinjen har samlats in från förrådet"
msgid ""
"The following responsibilities, by author, were found in the current revision "
"of the repository (comments are excluded from the line count, if possible)"
msgstr ""
"Följande ansvar, utefter upphovsman, hittades i den nuvarande revisionen av "
"förrådet (kommentarer är uteslutna från radberäkningen, om så möjligt)"
msgid "The given option argument is not a valid boolean."
msgstr "Det angivna flaggargumentet är inte en giltig boolean."
#, python-brace-format
msgid ""
"The output has been generated by {0} {1}. The statistical analysis tool for "
"git repositories."
msgstr ""
"Sidan har skapats av {0} {1}; det statistiska analysverktyget för git-förråd."
#, python-brace-format
msgid "Try `{0} --help' for more information."
msgstr "Försök med `{0} --help' för mer information."
msgid "Unable to determine absolute path of git repository."
msgstr "Kunde inte fastställa den absoluta sökvägen till git-förrådet."
#, python-brace-format
msgid ""
"Usage: {0} [OPTION]... [REPOSITORY]\n"
"List information about the repository in REPOSITORY. If no repository is\n"
"specified, the current directory is used. If multiple repositories are\n"
"given, information will be fetched from the last repository specified.\n"
"\n"
"Mandatory arguments to long options are mandatory for short options too.\n"
"Boolean arguments can only be given to long options.\n"
" -c, --checkout-missing[=BOOL] try to checkout any missing files\n"
" -f, --file-types=EXTENSIONS a comma separated list of file extensions "
"to\n"
" -f, --file-types=EXTENSIONS a comma separated list of file extensions to\n"
" include when computing statistics. The\n"
" default extensions used are:\n"
" {1}\n"
" Specifying * includes files with no\n"
" extension, while ** includes all files\n"
" -F, --format=FORMAT define in which format output should be\n"
" generated; the default format is 'text' "
"and\n"
" generated; the default format is 'text' and\n"
" the available formats are:\n"
" {2}\n"
" --grading[=BOOL] show statistics and information in a way "
"that\n"
" --grading[=BOOL] show statistics and information in a way that\n"
" is formatted for grading of student\n"
" projects; this is the same as supplying "
"the\n"
" projects; this is the same as supplying the\n"
" options -HlmrTw\n"
" -H, --hard[=BOOL] track rows and look for duplicates harder;\n"
" this can be quite slow with big "
"repositories\n"
" -l, --list-file-types[=BOOL] list all the file extensions available in "
"the\n"
" this can be quite slow with big repositories\n"
" -l, --list-file-types[=BOOL] list all the file extensions available in the\n"
" current branch of the repository\n"
" -L, --localize-output[=BOOL] localize the generated output to the "
"selected\n"
" -L, --localize-output[=BOOL] localize the generated output to the selected\n"
" system language if a translation is\n"
" available\n"
" -m --metrics[=BOOL] include checks for certain metrics during "
"the\n"
" -m --metrics[=BOOL] include checks for certain metrics during the\n"
" analysis of commits\n"
" -r --responsibilities[=BOOL] show which files the different authors "
"seem\n"
" -r --responsibilities[=BOOL] show which files the different authors seem\n"
" most responsible for\n"
" --since=DATE only show statistics for commits more "
"recent\n"
" --since=DATE only show statistics for commits more recent\n"
" than a specific date\n"
" -T, --timeline[=BOOL] show commit timeline, including author "
"names\n"
" --until=DATE only show statistics for commits older than "
"a\n"
" -T, --timeline[=BOOL] show commit timeline, including author names\n"
" --until=DATE only show statistics for commits older than a\n"
" specific date\n"
" -w, --weeks[=BOOL] show all statistical information in weeks\n"
" instead of in months\n"
" -x, --exclude=PATTERN an exclusion pattern describing file names\n"
" that should be excluded from the "
"statistics;\n"
" can be specified multiple times\n"
" -x, --exclude=PATTERN an exclusion pattern describing the file\n"
" paths, revisions, revisions with certain\n"
" commit messages, author names or author\n"
" emails that should be excluded from the\n"
" statistics; can be specified multiple times\n"
" -h, --help display this help and exit\n"
" --version output version information and exit\n"
"\n"
@ -249,58 +275,52 @@ msgid ""
msgstr ""
"Användning: {0} [FLAGGA]... [KATALOG]\n"
"Lista information om förrådet i KATALOG. Om ingen katalog anges så\n"
"används den nuvarande katalogen istället. Om flera förråd anges så hämtas\n"
"används den nuvarande katalogen istället. Om flera kataloger anges så "
"hämtas\n"
"information från den sista katalogen som angivits.\n"
"\n"
"Obligatoriska argument till långa flaggor är obligatoriska även för de "
"korta.\n"
"Booleska argument kan bara ges till långa flaggor.\n"
" -c, --checkout-missing=[BOOL] försök att checka ut filer som saknas\n"
" -f, --file-types=FILSUFFIX en komma-separerad lista av fil-suffix som\n"
" ska inkluderas vid statistikberäkning. "
"De\n"
" ska inkluderas vid statistikberäkning. De\n"
" förvalda suffixen är följande:\n"
" {1}\n"
" Anges * så inkluderas även filer utan\n"
" fil-suffix, medan ** inkluderar alla filer\n"
" oavsett fil-suffix\n"
" -F, --format=FORMAT ange i vilket format den genererade\n"
" utmatningen ska vara; det förvalda "
"formatet\n"
" utmatningen ska vara; det förvalda formatet\n"
" är 'text', de tillgängliga formaten är:\n"
" {2}\n"
" --grading[=BOOL] visa statistik och information anpassad "
"för\n"
" rättning av studentprojekt; detta är\n"
" detsamma som att ange flaggorna -HlmrTw\n"
" -H, --hard[=BOOL] spåra rader och leta efter dubbletter "
"hårdare;\n"
" -H, --hard[=BOOL] spåra rader och leta efter dubbletter hårdare;\n"
" detta kan ta lång tid på stora förråd\n"
" -l, --list-file-types[=BOOL] lista alla fil-suffix som hittades i den\n"
" nuvarande grenen i förrådet\n"
" -L --localize-output[=BOOL] Översätt den genererade utmatningen till "
"det\n"
" nuvarande systemspråket om en "
"översättning\n"
" -L --localize-output[=BOOL] översätt den genererade utmatningen till det\n"
" nuvarande systemspråket om en översättning\n"
" finns tillgänglig\n"
" -m --metrics[=BOOL] inkludera kontroller för kodmetrik vid\n"
" analysen av inlämningar\n"
" -r --responsibilities[=BOOL] visa vilka filer olika upphovsmän verkar "
"mest\n"
" -r --responsibilities[=BOOL] visa vilka filer olika upphovsmän verkar mest\n"
" ansvariga för\n"
" --since=DATUM beräkna endast statistik för inlämningar "
"nyare\n"
" --since=DATUM beräkna endast statistik för inlämningar nyare\n"
" än ett angivet datum\n"
" -T, --timeline[=BOOL] visa en inlämningstidslinje för alla funna\n"
" upphovsmän\n"
" --until=DATUM beräkna endast statistik för inlämningar "
"äldre\n"
" --until=DATUM beräkna endast statistik för inlämningar äldre\n"
" än ett angivet datum\n"
" -w, --weeks[=BOOL] visa statistisk information indelad i "
"veckor\n"
" -w, --weeks[=BOOL] visa statistisk information indelad i veckor\n"
" istället för månader\n"
" -x, --exclude=MÖNSTER ett uteslutningsmönster som anger de "
"filnamn\n"
" som ska uteslutas ur statistiken; kan "
"anges\n"
" flera gånger\n"
" -x, --exclude=MÖNSTER ett uteslutningsmönster som anger filsökvägar,\n"
" inlämningsrevisioner, inlämningsrevisioner\n"
" med angivna kommentarer, upphovsmän eller\n"
" e-post adresser som ska uteslutas ur\n"
" statistiken; kan anges flera gånger\n"
" -h, --help visa denna hjälptext och avsluta\n"
" --version visa versionsinformation och avsluta\n"
"\n"
@ -310,3 +330,41 @@ msgstr ""
"\n"
"gitinspector kräver att den körbara filen för git finns i PATH.\n"
"Rapportera fel i gitinspector till gitinspector@ejwa.se."
msgid ""
"WARNING: The terminal encoding is not correctly configured. gitinspector "
"might malfunction. The encoding can be configured with the environment "
"variable 'PYTHONIOENCODING'."
msgstr ""
"VARNING: Terminalens teckenkodning är inte korrekt konfigurerad; "
"gitinspector kanske därför inte fungerar korrekt. Teckenkodningen kan "
"konfigureras med miljövariabeln 'PYTHONIOENCODING'."
msgid "XML output not yet supported in"
msgstr "XML-utmatning stöds inte i"
#, python-brace-format
msgid ""
"gitinspector requires at least Python 2.6 to run (version {0} was found)."
msgstr "gitinspector kräver åtminstone Python 2.6 (version {0} hittades)."
msgid "invalid regular expression specified"
msgstr "ogiltigt reguljärt uttryck angivet"
msgid "is mostly responsible for"
msgstr "är mestandels ansvarig för"
msgid "specified output format not supported."
msgstr "det angivna utmatningsformatet stöds inte."
#, python-brace-format
msgid "{0} ({1:.3f} in cyclomatic complexity density)"
msgstr "{0} ({1:.3f} i kyklomatisk komplexitetsdensitet)"
#, python-brace-format
msgid "{0} ({1} estimated lines of code)"
msgstr "{0} ({1} beräknade antal rader)"
#, python-brace-format
msgid "{0} ({1} in cyclomatic complexity)"
msgstr "{0} ({1} i kyklomatisk komplexitet)"

View File

@ -1,4 +1,4 @@
# Copyright © 2012-2013 Ejwa Software. All rights reserved.
# Copyright © 2012-2015 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -15,71 +15,55 @@
# You should have received a copy of the GNU General Public License
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
#
# Bill Wang <wangzhijiebill@gmail.com>, 2013
# Bill Wang <wangzhijiebill@gmail.com>, 2013-2015
msgid ""
msgstr ""
"Project-Id-Version: gitinspector 0.3.1\n"
"Project-Id-Version: gitinspector 0.5.0dev\n"
"Report-Msgid-Bugs-To: gitinspector@ejwa.se\n"
"POT-Creation-Date: 2013-07-22 03:45+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"POT-Creation-Date: 2015-10-02 03:35+0200\n"
"PO-Revision-Date: 2015-20-23 18:33-0500\n"
"Last-Translator: Bill Wang <wangzhijiebill@gmail.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language-Team: Chinese <>\n"
msgid "The extensions below were found in the repository history"
msgstr "在资料库中扎到下列插件"
#, python-format
msgid "% in comments"
msgstr "% 的注释"
#, python-format
msgid "% of changes"
msgstr "% 的修改"
msgid "(extensions used during statistical analysis are marked)"
msgstr "(统计分析过程中使用的插件已标记)"
msgid "The following historical commit information, by author, was found in the repository"
msgstr "在资料库中找到下列提交记录,已按作者分类"
msgid "No commited files with the specified extensions were found"
msgstr "未找到符合条件的已提交文件"
msgid "Age"
msgstr "年龄"
msgid "Author"
msgstr "作者"
msgid "Below are the number of rows from each author that have survived and are still intact in the current revision"
msgstr "下面是过于每位作者在现在的版本了存留下来的行数"
#, python-brace-format
msgid "Checking how many rows belong to each author (Progress): {0:.0f}%"
msgstr "检查每位作者贡献了多少行 (进度): {0:.0f}"
msgid "Commits"
msgstr "提交"
msgid "Insertions"
msgstr "加入"
msgid "Deletions"
msgstr "删除"
#, python-format
msgid "% of changes"
msgstr "% 个修改"
msgid "Minor Authors"
msgstr "次要作者"
msgid "specified output format not supported."
msgstr "不支持设定的输出文件格式"
msgid ""
"The following repsonsibilties, by author, were found in the current revision of the repository (comments are exluded from the "
"line count, if possible)"
msgstr "在最新的资料库修改中,找到下列分工,按作者分类。 (在计算代码数量时尽可能的排除了批注)"
msgid "is mostly responsible for"
msgstr "主要分工是"
msgid ""
"Copyright © 2012-2013 Ejwa Software. All rights reserved.\n"
"Copyright © 2012-2015 Ejwa Software. All rights reserved.\n"
"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n"
"\n"
"Written by Adam Waldenberg."
msgstr ""
"版权所有 © 2012-2013 Ejwa Software. 保留所有权利。\n"
"版权所有 © 2012-2014 Ejwa Software. 保留所有权利。\n"
"GPLv3的许可证+GNU GPL版本3或更高版本<http://gnu.org/licenses/gpl.html>。\n"
"这是自由软件:您可以自由地更改并重新发布它。\n"
"在法律允许的范围内,没有 任何 保证。\n"
@ -87,90 +71,122 @@ msgstr ""
"撰写: Adam Waldenberg.\n"
"翻译: 王之杰 Bill Wang"
msgid "gitinspector requires at least Python 2.6 to run (version {0} was found)."
msgstr "gitinspector 要求 Python版本至少2.6 去运行 (已找到版本 {0}) "
msgid "Deletions"
msgstr "删除"
msgid "Try `{0} --help' for more information."
msgstr "尝试 `{0} --help' 已获得更多信息"
msgid "The output has been generated by {0}; the statistical analysis tool for git repositories."
msgstr "输出结果由 {0}的统计分析组建为git数据库生成 "
msgid "Show minor authors"
msgstr "显示次要作者"
msgid "Hide minor authors"
msgstr "隐藏次要作者"
msgid "Show rows with minor work"
msgstr "显示次要工作栏"
msgid "Hide rows with minor work"
msgstr "隐藏次要工作栏"
#, python-format
msgid "Error processing git repository at \"%s\"."
msgstr "在处理\"%s\"库时出现问题"
msgid "HTML output not yet supported in"
msgstr "HTML文本输出暂不支持"
msgid "Text output not yet supported in"
msgstr "TEXT文本输出暂不支持"
msgid "Hide minor authors"
msgstr "隐藏次要作者"
msgid "XML output not yet supported in"
msgstr "XML文本输出暂不支持"
msgid "Hide rows with minor work"
msgstr "隐藏次要工作"
msgid ""
"The following files were missing in the repository and were therefore not completely included in the statistical analysis. To "
"include them, you can either checkout manually using git or use the -c option in gitinspector"
msgstr ""
"在数据库中下列文件丢失,因此不完全包括在统计分析中。 你可以用git手动检出或者"
"在 gitinspector 中用 -c 选项 "
msgid "Insertions"
msgstr "添加"
msgid "The following files are suspiciously big (in order of severity)"
msgstr "下列文件过大文件有些可疑 (按严重性排列)"
msgid "Minor Authors"
msgstr "次要作者"
msgid "Modified Rows:"
msgstr "修改过的行"
msgid "No commited files with the specified extensions were found"
msgstr "未找到符合条件的已提交文件"
msgid "No metrics violations were found in the repository"
msgstr "未在数据库中发现违反指标"
#, python-brace-format
msgid "Repository statistics for {0}"
msgstr "关于{0}代码库统计数据"
msgid "Rows"
msgstr "行数"
msgid "Show minor authors"
msgstr "显示次要作者"
msgid "Show rows with minor work"
msgstr "显示次要行"
msgid "Stability"
msgstr "稳定性"
#, python-brace-format
msgid "Statistical information for the repository '{0}' was gathered on {1}."
msgstr "关于{0}的统计数据基于{1}上信息"
msgid "Text output not yet supported in"
msgstr "文本输出暂不支持"
msgid "The authors with the following emails were excluded from the statistics due to the specified exclusion patterns"
msgstr "下列邮箱相关联的作者已经从数据统计中移除"
msgid "The extensions below were found in the repository history"
msgstr "在资料库中找到下列扩展名"
msgid "The following authors were excluded from the statistics due to the specified exclusion patterns"
msgstr "下列作者已按规则从数据统计中移除"
msgid "The following commit revisions were excluded from the statistics due to the specified exclusion patterns"
msgstr "下列提交已按规则从数据统计中移除"
msgid "The following files are suspiciously big (in order of severity)"
msgstr "下列文件过大文件有些可疑 (按严重程度排列)"
msgid "The following files have an elevated cyclomatic complexity (in order of severity)"
msgstr "下列文件过度循环性问题 (按严重程度排列)"
msgid "The following files have an elevated cyclomatic complexity density (in order of severity)"
msgstr "下列文件度循环性过度密集 (按严重程度排列)"
msgid "The following files were excluded from the statistics due to the specified exclusion patterns"
msgstr "下列文件已按排除规律移除数据统计"
msgid "The following historical commit information, by author, was found in the repository"
msgstr "在资料库中找到下列提交记录,已按作者分类"
msgid "The following history timeline has been gathered from the repository"
msgstr "在数据库中收集到下列时间轴"
msgid "Modified Rows:"
msgstr "被更改过的行"
msgid "Checking how many rows belong to each author (Progress): {0:.0f}%"
msgstr "检查每位作者贡献了多少行 (进度): {0:.0f}%"
msgid "Below are the number of rows from each author that have survived and are still intact in the current revision"
msgstr "下面是过于每位作者在现在的版本了存留下来的行数"
msgid "Rows"
msgstr "行"
#, python-format
msgid "% in comments"
msgstr "% 批注"
msgid "The following responsibilities, by author, were found in the current revision of the repository (comments are excluded from the line count, if possible)"
msgstr "在最新的资料库修改中,找到下列分工,按作者分类。 (在计算代码数量时尽可能的排除了批注)"
msgid "The given option argument is not a valid boolean."
msgstr "给予的选项不是一个有效的 布尔值 boolean"
msgid "invalid regular expression specified"
msgstr "无效的正则表达式"
#, python-brace-format
msgid "The output has been generated by {0} {1}. The statistical analysis tool for git repositories."
msgstr "导出结果已基于{0}{1}生成。Git数据统计软件。"
msgid "The following files were excluded from the statistics due to the specified exclusion patterns"
msgstr "根据指定的排除模式,下列文件被排除在统计中"
#, python-brace-format
msgid "Try `{0} --help' for more information."
msgstr "尝试 `{0} --help' 已获得更多信息"
msgid "Unable to determine absolute path of git repository."
msgstr "无法确定git库的绝对路径"
#, python-brace-format
msgid ""
"Usage: {0} [OPTION]... [DIRECTORY]\n"
"List information about the repository in DIRECTORY. If no directory is\n"
"specified, the current directory is used. If multiple directories are\n"
"given, information will be fetched from the last directory specified.\n"
"Usage: {0} [OPTION]... [REPOSITORY]\n"
"List information about the repository in REPOSITORY. If no repository is\n"
"specified, the current directory is used. If multiple repositories are\n"
"given, information will be fetched from the last repository specified.\n"
"\n"
"Mandatory arguments to long options are mandatory for short options too.\n"
"Boolean arguments can only be given to long options.\n"
" -c, --checkout-missing[=BOOL] try to checkout any missing files\n"
" -f, --file-types=EXTENSIONS a comma separated list of file extensions to\n"
" include when computing statistics. The\n"
" default extensions used are:\n"
" {1}\n"
" Specifying * includes files with no\n"
" extension, while ** includes all files\n"
" -F, --format=FORMAT define in which format output should be\n"
" generated; the default format is 'text' and\n"
" the available formats are:\n"
@ -197,9 +213,11 @@ msgid ""
" specific date\n"
" -w, --weeks[=BOOL] show all statistical information in weeks\n"
" instead of in months\n"
" -x, --exclude=PATTERN an exclusion pattern describing file names\n"
" that should be excluded from the statistics;\n"
" can be specified multiple times\n"
" -x, --exclude=PATTERN an exclusion pattern describing the file\n"
" paths, revisions, revisions with certain\n"
" commit messages, author names or author\n"
" emails that should be excluded from the\n"
" statistics; can be specified multiple times\n"
" -h, --help display this help and exit\n"
" --version output version information and exit\n"
"\n"
@ -210,14 +228,15 @@ msgid ""
"gitinspector requires that the git executable is available in your PATH.\n"
"Report gitinspector bugs to gitinspector@ejwa.se."
msgstr ""
"用法: {0} [选项]... [目录]\n"
"用法:{0} [选项]... [目录] \n"
"在目录列出有关库的信息,如果没有指定目录,那么将使用现目录。如果有多个目录,\n"
"将采用指定的最后一个目录\n"
"\n"
"长选项的强制性参数对短选项也适用\n"
"布尔参数只能给予长选项\n"
" -c, --checkout-missing[=BOOL] 尝试检出任何丢失的文件\n"
" -f, --file-types=EXTENSIONS 一串逗号分隔的文件类型\n"
" 用 来包含无扩展名的文件\n"
" 用 包涵所有文件\n"
" 这些文件将会被用于计算统计数据.\n"
" 默认文件类型:\n"
" {1}\n"
@ -239,13 +258,45 @@ msgstr ""
" --until=DATE 只显示特定时间前的结果\n"
" -w, --weeks[=BOOL] 按周来显示统计数据,而非月\n"
" -x, --exclude=PATTERN 按特定格式排除不应该被统计\n"
" 的文件;可以重复\n"
" -h, --help 现实这个帮助信息并退出\n"
" --version 现实版本信息并退出\n"
" 的文件,作者名字或邮箱;可以按文件名,\n"
" 作者名,作者邮箱,提交信息,路径和版本。可以重复\n"
" -h, --help 显示这个帮助信息并退出\n"
" --version 显示版本信息并退出\n"
"\n"
"gitinspector 会过滤信息并且仅统计那些修改,增加或减少,指定文件类型的提交,\n"
"如需详细信息,请参考 -f 或 --file-types 选项\n"
"\n"
"gitinspector 需要 git 可运行文件 在 PATH 中.\n"
"错误报告,请寄 gitinspector@ejwa.se.\n"
"翻译错误,请寄 wangzhijiebill@gmail.com."
"翻译错误,请寄 wangzhijiebill@gmail.com"
msgid "WARNING: The terminal encoding is not correctly configured. gitinspector might malfunction. The encoding can be configured with the environment variable 'PYTHONIOENCODING'."
msgstr "警告命令指示符编码格式有误。gitinspector可能出错。编码格式可以在环境变量的'PYTHONIOENCODING'下修改"
msgid "XML output not yet supported in"
msgstr "XML文本输出暂不支持"
#, python-brace-format
msgid "gitinspector requires at least Python 2.6 to run (version {0} was found)."
msgstr "gitinspector 要求 Python版本至少2.6 (已找到版本 {0}) "
msgid "invalid regular expression specified"
msgstr "无效的正则表达式"
msgid "is mostly responsible for"
msgstr "主要分工是"
msgid "specified output format not supported."
msgstr "不支持设定的输出文件格式"
#, python-brace-format
msgid "{0} ({1:.3f} in cyclomatic complexity density)"
msgstr "{0} (循环型结构密集度{1: 3f})"
#, python-brace-format
msgid "{0} ({1} estimated lines of code)"
msgstr "{0}(估测代码行数{1}"
#, python-brace-format
msgid "{0} ({1} in cyclomatic complexity)"
msgstr "{0}(代码循环性结构{1}"

View File

@ -1,6 +1,6 @@
# coding: utf-8
#
# Copyright © 2012-2013 Ejwa Software. All rights reserved.
# Copyright © 2012-2015 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -19,17 +19,12 @@
from __future__ import print_function
from __future__ import unicode_literals
from . import localization
localization.init()
try:
import localization
localization.init()
except:
import gitinspector.localization
gitinspector.localization.init()
__version__ = "0.5.0dev"
__version__ = "0.3.1"
__doc__ = _("""Copyright © 2012-2013 Ejwa Software. All rights reserved.
__doc__ = _("""Copyright © 2012-2015 Ejwa Software. All rights reserved.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

64
package.json Normal file
View File

@ -0,0 +1,64 @@
{
"name": "gitinspector",
"version": "0.5.0-dev-1",
"description": "Gitinspector is a statistical analysis tool for git repositories. The default analysis shows general statistics per author, which can be complemented with a timeline analysis that shows the workload and activity of each author.",
"preferGlobal": true,
"main": "gitinspector.py",
"directories": {
"doc": "docs",
"test": "tests"
},
"scripts": {
"clean": "rimraf **/*.pyc",
"crlf": "crlf --set=LF **/*.py",
"prepublish": "npm run clean && npm run crlf",
"release": "with-package git commit -am pkg.version && with-package git tag pkg.version && git push && npm publish && git push --tags",
"release:beta": "npm run release && npm run tag:beta",
"tag:beta": "with-package npm dist-tag add pkg.name@pkg.version beta",
"test": "echo \"Error: no test specified\" && exit 1"
},
"bin": {
"gitinspector": "gitinspector.py"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ejwa/gitinspector.git"
},
"keywords": [
"git",
"statistics",
"stats",
"analytics",
"grading"
],
"author": {
"name": "Adam Waldenberg",
"email": "adam.waldenberg@ejwa.se",
"url": "https://github.com/adam-waldenberg"
},
"contributors": [
"Agustín Cañas",
"Bart van Andel <bavanandel@gmail.com>",
"Bill Wang",
"Christian Kastner",
"Jiwon Kim",
"Kamila Chyla",
"Luca Motta",
"Philipp Nowak",
"Sergei Lomakov",
"Yannick Moy"
],
"license": "GPL-3.0",
"bugs": {
"url": "https://github.com/ejwa/gitinspector/issues"
},
"homepage": "https://github.com/ejwa/gitinspector#readme",
"devDependencies": {
"crlf": "^1.1.0",
"rimraf": "^2.5.4",
"with-package": "^0.2.0"
},
"dependencies": {
"python-shell": "^0.4.0"
}
}

View File

@ -34,7 +34,7 @@ setup(
description = ("A statistical analysis tool for git repositories."),
license = "GNU GPL v3",
keywords = "analysis analyzer git python statistics stats vc vcs timeline",
url = "http://gitinspector.googlecode.com",
url = "https://github.com/ejwa/gitinspector",
long_description = read("DESCRIPTION.txt"),
classifiers = [
"Development Status :: 4 - Beta",
@ -48,5 +48,5 @@ setup(
package_data = {"": ["html/*", "translations/*"]},
data_files = [("share/doc/gitinspector", glob("*.txt"))],
entry_points = {"console_scripts": ["gitinspector = gitinspector.gitinspector:main"]},
zip_safe = True
zip_safe = False
)

View File

@ -1,6 +1,6 @@
# coding: utf-8
#
# Copyright © 2013 Ejwa Software. All rights reserved.
# Copyright © 2013-2015 Ejwa Software. All rights reserved.
#
# This file is part of gitinspector.
#
@ -41,10 +41,10 @@ def __test_extension__(commented_file, extension):
class TexFileTest(unittest2.TestCase):
def test(self):
comment_counter = __test_extension__("/commented_file.tex", "tex")
comment_counter = __test_extension__("/resources/commented_file.tex", "tex")
self.assertEqual(comment_counter, 30)
class CppFileTest(unittest2.TestCase):
def test(self):
comment_counter = __test_extension__("/commented_file.cpp", "cpp")
comment_counter = __test_extension__("/resources/commented_file.cpp", "cpp")
self.assertEqual(comment_counter, 25)