From 27c24a3d758d254a978ad8ad016148d8928085a0 Mon Sep 17 00:00:00 2001 From: Jason Robinson Date: Sun, 24 Jan 2016 15:04:08 +0200 Subject: [PATCH 1/2] Add a .gitignore --- .gitignore | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..faed70d --- /dev/null +++ b/.gitignore @@ -0,0 +1,123 @@ +### Python template +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio + +*.iml + +## Directory-based project format: +.idea/ +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/dictionaries + +# Sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml +# .idea/uiDesigner.xml + +# Gradle: +# .idea/gradle.xml +# .idea/libraries + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +### SublimeText template +# cache files for sublime text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache + +# workspace files are user-specific +*.sublime-workspace + +# project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using SublimeText +# *.sublime-project + +# sftp configuration file +sftp-config.json + +# Created by .ignore support plugin (hsz.mobi) From e9d16e0626e3998fa02b0f0843c53f5a64bc7274 Mon Sep 17 00:00:00 2001 From: Jason Robinson Date: Sun, 24 Jan 2016 15:04:33 +0200 Subject: [PATCH 2/2] Implement support for filtering out merge commits Using option no_merges, default True --- gitstats | 79 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/gitstats b/gitstats index c71b0e4..85a06a8 100755 --- a/gitstats +++ b/gitstats @@ -47,7 +47,8 @@ conf = { 'linear_linestats': 1, 'project_name': '', 'processes': 8, - 'start_date': '' + 'start_date': '', + 'no_merges': 0, } def getpipeoutput(cmds, quiet = False): @@ -133,7 +134,7 @@ def getnumoflinesinblob(ext_blob): ext, blob_id = ext_blob return (ext, blob_id, int(getpipeoutput(['git cat-file blob %s' % blob_id, 'wc -l']).split()[0])) -class DataCollector: +class DataCollector(object): """Manages data collection from a revision control repository.""" def __init__(self): self.stamp_created = time.time() @@ -201,7 +202,7 @@ class DataCollector: self.projectname = os.path.basename(os.path.abspath(dir)) else: self.projectname = conf['project_name'] - + ## # Load cacheable data def loadCache(self, cachefile): @@ -216,7 +217,7 @@ class DataCollector: f.seek(0) self.cache = pickle.load(f) f.close() - + ## # Produce any additional statistics from the extracted data. def refine(self): @@ -226,7 +227,7 @@ class DataCollector: # : get a dictionary of author def getAuthorInfo(self, author): return None - + def getActivityByDayOfWeek(self): return {} @@ -241,31 +242,31 @@ class DataCollector: # Get a list of authors def getAuthors(self): return [] - + def getFirstCommitDate(self): return datetime.datetime.now() - + def getLastCommitDate(self): return datetime.datetime.now() - + def getStampCreated(self): return self.stamp_created - + def getTags(self): return [] - + def getTotalAuthors(self): return -1 - + def getTotalCommits(self): return -1 - + def getTotalFiles(self): return -1 - + def getTotalLOC(self): return -1 - + ## # Save cacheable data def saveCache(self, cachefile): @@ -283,10 +284,14 @@ class DataCollector: os.rename(tempfile, cachefile) class GitDataCollector(DataCollector): + def __init__(self): + super(GitDataCollector, self).__init__() + self.no_merges = "--no-merges" if conf["no_merges"] else "" + def collect(self, dir): DataCollector.collect(self, dir) - self.total_authors += int(getpipeoutput(['git shortlog -s %s' % getlogrange(), 'wc -l'])) + self.total_authors += int(getpipeoutput(['git shortlog -s %s %s' % (getlogrange(), self.no_merges), 'wc -l'])) #self.total_lines = int(getoutput('git-ls-files -z |xargs -0 cat |wc -l')) # tags @@ -311,7 +316,7 @@ class GitDataCollector(DataCollector): tags_sorted_by_date_desc = map(lambda el : el[1], reversed(sorted(map(lambda el : (el[1]['date'], el[0]), self.tags.items())))) prev = None for tag in reversed(tags_sorted_by_date_desc): - cmd = 'git shortlog -s "%s"' % tag + cmd = 'git shortlog -s "%s" %s' % (tag, self.no_merges) if prev != None: cmd += ' "^%s"' % prev output = getpipeoutput([cmd]) @@ -327,7 +332,7 @@ class GitDataCollector(DataCollector): # Collect revision statistics # Outputs "