[](https://github.com/asottile/git-code-debt/actions/workflows/main.yml)
[](https://results.pre-commit.ci/latest/github/asottile/git-code-debt/main)
git-code-debt
=============
A dashboard for monitoring code debt in a git repository.
## Installation
```bash
pip install git-code-debt
```
## Usage
### Basic / tl;dr Usage
#### make a `generate_config.yaml`
```yaml
# required: repository to clone (can be anything `git clone` understands) even
# a repository already on disk
repo: git@github.com:asottile/git-code-debt
# required: database generation path
database: database.db
# optional: default False
skip_default_metrics: false
# optional: default []
metric_package_names: []
# optional: default ^$ (python regex) to exclude paths such as '^vendor/'
exclude: ^$
```
#### invoke the cli
```
# Generate code metric data (substitute your own repo path)
$ git-code-debt-generate
# Start the server
$ git-code-debt-server database.db
```
### Updating data on an existing database
Adding data to the database is as simple as running generate again.
`git-code-debt` will pick up in the git history from where data was generated
previously.
```
$ git-code-debt-generate
```
### Creating your own metrics
1. Create a python project which adds `git-code-debt` as a dependency.
2. Create a package where you'll write your metrics
3. Add your package to `metric_package_names` in your `generate_config.yaml`
The simplest way to write your own custom metrics is to extend
`git_code_debt.metrics.base.SimpleLineCounterBase`.
Here's what the base class looks like
```python
class SimpleLineCounterBase(DiffParserBase):
# ...
def should_include_file(self, file_diff_stat: FileDiffStat) -> bool:
"""Implement me to return whether a filename should be included.
By default, this returns True.
:param FileDiffStat file_diff_stat:
"""
return True
def line_matches_metric(self, line: bytes, file_diff_stat: FileDiffStat) -> bool:
"""Implement me to return whether a line matches the metric.
:param bytes line: Line in the file
:param FileDiffStat file_diff_stat:
"""
raise NotImplementedError
```
Here's an example metric
```python
from git_code_debt.metrics.base import SimpleLineCounterBase
class Python__init__LineCount(SimpleLineCounterBase):
"""Counts the number of lines in __init__.py"""
def should_include_file(self, file_diff_stat: FileDiffStat) -> bool:
return file_diff_stat.filename == b'__init__.py'
def line_matches_metric(self, line: bytes, file_diff_stat -> FileDiffStat) -> bool:
# All lines in __init__.py match
return True
```
An additional class is provided which feeds lines as text
(`SimpleLineCounterBase` presents them as `bytes`): `TextLineCounterBase`.
Here is an example metric using that base class:
```python
from git_code_debt.metrics.base import TextLineCounterBase
class XXXLineCount(TextLineCounterBase):
"""Counts the number of lines which are XXX comments"""
def text_line_matches_metric(self, line: str, file_diff_stat: FileDiffStat) -> bool:
return '# XXX' in line
```
More complex metrics can extend `DiffParserBase`
```python
class DiffParserBase(object):
# Specify __metric__ = False to not be included (useful for base classes)
__metric__ = False
def get_metrics_from_stat(self, commit: Commit, file_diff_stats: Tuple[FileDiffStat, ...]) -> bool:
"""Implement me to yield Metric objects from the input list of
FileStat objects.
Args:
commit - Commit object
file_diff_stats - list of FileDiffStat objects
Returns:
generator of Metric objects
"""
raise NotImplementedError
def get_metrics_info(self) -> List[MetricInfo]:
"""Implement me to yield `MetricInfo` objects."""
raise NotImplementedError
```
## Some screenshots
### Index

### Graph

Raw data
{
"_id": null,
"home_page": "https://github.com/asottile/git-code-debt",
"name": "git-code-debt",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": null,
"author": "Anthony Sottile",
"author_email": "asottile@umich.edu",
"download_url": "https://files.pythonhosted.org/packages/57/14/f12ee2a994bc5035596527582fa03fdba084f01c74cf9e55ee655f81ea77/git_code_debt-1.2.0.tar.gz",
"platform": null,
"description": "[](https://github.com/asottile/git-code-debt/actions/workflows/main.yml)\n[](https://results.pre-commit.ci/latest/github/asottile/git-code-debt/main)\n\ngit-code-debt\n=============\n\nA dashboard for monitoring code debt in a git repository.\n\n\n## Installation\n\n```bash\npip install git-code-debt\n```\n\n\n## Usage\n\n\n### Basic / tl;dr Usage\n\n#### make a `generate_config.yaml`\n\n```yaml\n# required: repository to clone (can be anything `git clone` understands) even\n# a repository already on disk\nrepo: git@github.com:asottile/git-code-debt\n\n# required: database generation path\ndatabase: database.db\n\n# optional: default False\nskip_default_metrics: false\n\n# optional: default []\nmetric_package_names: []\n\n# optional: default ^$ (python regex) to exclude paths such as '^vendor/'\nexclude: ^$\n```\n\n#### invoke the cli\n\n```\n# Generate code metric data (substitute your own repo path)\n$ git-code-debt-generate\n# Start the server\n$ git-code-debt-server database.db\n```\n\n### Updating data on an existing database\n\nAdding data to the database is as simple as running generate again.\n`git-code-debt` will pick up in the git history from where data was generated\npreviously.\n\n```\n$ git-code-debt-generate\n```\n\n### Creating your own metrics\n\n1. Create a python project which adds `git-code-debt` as a dependency.\n2. Create a package where you'll write your metrics\n3. Add your package to `metric_package_names` in your `generate_config.yaml`\n\n\nThe simplest way to write your own custom metrics is to extend\n`git_code_debt.metrics.base.SimpleLineCounterBase`.\n\n\nHere's what the base class looks like\n\n```python\n\nclass SimpleLineCounterBase(DiffParserBase):\n # ...\n\n def should_include_file(self, file_diff_stat: FileDiffStat) -> bool:\n \"\"\"Implement me to return whether a filename should be included.\n By default, this returns True.\n\n :param FileDiffStat file_diff_stat:\n \"\"\"\n return True\n\n def line_matches_metric(self, line: bytes, file_diff_stat: FileDiffStat) -> bool:\n \"\"\"Implement me to return whether a line matches the metric.\n\n :param bytes line: Line in the file\n :param FileDiffStat file_diff_stat:\n \"\"\"\n raise NotImplementedError\n```\n\nHere's an example metric\n\n```python\nfrom git_code_debt.metrics.base import SimpleLineCounterBase\n\n\nclass Python__init__LineCount(SimpleLineCounterBase):\n \"\"\"Counts the number of lines in __init__.py\"\"\"\n\n def should_include_file(self, file_diff_stat: FileDiffStat) -> bool:\n return file_diff_stat.filename == b'__init__.py'\n\n def line_matches_metric(self, line: bytes, file_diff_stat -> FileDiffStat) -> bool:\n # All lines in __init__.py match\n return True\n```\n\nAn additional class is provided which feeds lines as text\n(`SimpleLineCounterBase` presents them as `bytes`): `TextLineCounterBase`.\nHere is an example metric using that base class:\n\n```python\nfrom git_code_debt.metrics.base import TextLineCounterBase\n\n\nclass XXXLineCount(TextLineCounterBase):\n \"\"\"Counts the number of lines which are XXX comments\"\"\"\n\n def text_line_matches_metric(self, line: str, file_diff_stat: FileDiffStat) -> bool:\n return '# XXX' in line\n```\n\nMore complex metrics can extend `DiffParserBase`\n\n```python\nclass DiffParserBase(object):\n # Specify __metric__ = False to not be included (useful for base classes)\n __metric__ = False\n\n def get_metrics_from_stat(self, commit: Commit, file_diff_stats: Tuple[FileDiffStat, ...]) -> bool:\n \"\"\"Implement me to yield Metric objects from the input list of\n FileStat objects.\n\n Args:\n commit - Commit object\n file_diff_stats - list of FileDiffStat objects\n\n Returns:\n generator of Metric objects\n \"\"\"\n raise NotImplementedError\n\n def get_metrics_info(self) -> List[MetricInfo]:\n \"\"\"Implement me to yield `MetricInfo` objects.\"\"\"\n raise NotImplementedError\n```\n\n\n## Some screenshots\n\n### Index\n\n\n### Graph\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A dashboard for monitoring code debt in a git repository.",
"version": "1.2.0",
"project_urls": {
"Homepage": "https://github.com/asottile/git-code-debt"
},
"split_keywords": [],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "a47ccdc68a30e0916875350d4bfaf642436aee7ae62a341f51f64fddaa9d40f4",
"md5": "95e3fe585ed9a12ea9ce69ce4e6e0b4b",
"sha256": "af295bd69f33fb6000549af0daae32cef5b2953831dafedf0ae92166712a0cdc"
},
"downloads": -1,
"filename": "git_code_debt-1.2.0-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "95e3fe585ed9a12ea9ce69ce4e6e0b4b",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": ">=3.10",
"size": 70046,
"upload_time": "2025-10-10T17:17:32",
"upload_time_iso_8601": "2025-10-10T17:17:32.021765Z",
"url": "https://files.pythonhosted.org/packages/a4/7c/cdc68a30e0916875350d4bfaf642436aee7ae62a341f51f64fddaa9d40f4/git_code_debt-1.2.0-py2.py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "5714f12ee2a994bc5035596527582fa03fdba084f01c74cf9e55ee655f81ea77",
"md5": "eec560b909f066e920342a543678cb78",
"sha256": "c11caf38468960a1b71c13b90058a14a8614786d7a19d56cf1a6b1b580aff2fa"
},
"downloads": -1,
"filename": "git_code_debt-1.2.0.tar.gz",
"has_sig": false,
"md5_digest": "eec560b909f066e920342a543678cb78",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 56016,
"upload_time": "2025-10-10T17:17:33",
"upload_time_iso_8601": "2025-10-10T17:17:33.252498Z",
"url": "https://files.pythonhosted.org/packages/57/14/f12ee2a994bc5035596527582fa03fdba084f01c74cf9e55ee655f81ea77/git_code_debt-1.2.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-10 17:17:33",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "asottile",
"github_project": "git-code-debt",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"tox": true,
"lcname": "git-code-debt"
}