mailmap-checker 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,67 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ lint:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v6
17
+ - uses: astral-sh/setup-uv@v6
18
+ - uses: actions/setup-python@v6
19
+ with:
20
+ python-version: "3.13"
21
+ - run: uv sync
22
+ - run: uv run ruff check src tests
23
+ - run: uv run ruff format --check src tests
24
+
25
+ security:
26
+ runs-on: ubuntu-latest
27
+ steps:
28
+ - uses: actions/checkout@v6
29
+ - uses: astral-sh/setup-uv@v6
30
+ - uses: actions/setup-python@v6
31
+ with:
32
+ python-version: "3.13"
33
+ - run: uv sync
34
+ - run: uv run bandit -r src
35
+
36
+ test:
37
+ runs-on: ${{ matrix.os }}
38
+ strategy:
39
+ matrix:
40
+ os: [ubuntu-latest, macos-latest, windows-latest]
41
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
42
+ steps:
43
+ - uses: actions/checkout@v6
44
+ with:
45
+ fetch-depth: 0
46
+ - uses: astral-sh/setup-uv@v6
47
+ - uses: actions/setup-python@v6
48
+ with:
49
+ python-version: ${{ matrix.python-version }}
50
+ - run: uv sync
51
+ - run: uv run pytest --cov --cov-report=term-missing
52
+
53
+ commitlint:
54
+ runs-on: ubuntu-latest
55
+ if: github.event_name == 'pull_request'
56
+ steps:
57
+ - uses: actions/checkout@v6
58
+ with:
59
+ fetch-depth: 0
60
+ - uses: actions/setup-node@v6
61
+ with:
62
+ node-version: 22
63
+ - run: npm install @commitlint/cli @commitlint/config-conventional
64
+ - run: >
65
+ npx commitlint
66
+ --from ${{ github.event.pull_request.base.sha }}
67
+ --to ${{ github.event.pull_request.head.sha }}
@@ -0,0 +1,22 @@
1
+ name: Release
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ publish:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v6
15
+ - uses: astral-sh/setup-uv@v6
16
+ - uses: actions/setup-python@v6
17
+ with:
18
+ python-version: "3.13"
19
+ - run: uv build
20
+ - run: uv publish
21
+ env:
22
+ UV_PUBLISH_TOKEN: ${{ secrets.PYPI_PASSWORD }}
@@ -0,0 +1,15 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ venv/
7
+ .venv/
8
+ *.egg
9
+ .eggs/
10
+ .ruff_cache/
11
+ .pytest_cache/
12
+ .coverage
13
+ htmlcov/
14
+ node_modules/
15
+ uv.lock
@@ -0,0 +1,28 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v5.0.0
4
+ hooks:
5
+ - id: trailing-whitespace
6
+ - id: end-of-file-fixer
7
+ - id: check-yaml
8
+
9
+ - repo: https://github.com/astral-sh/ruff-pre-commit
10
+ rev: v0.11.4
11
+ hooks:
12
+ - id: ruff
13
+ args: [--fix]
14
+ - id: ruff-format
15
+
16
+ - repo: https://github.com/PyCQA/bandit
17
+ rev: 1.8.3
18
+ hooks:
19
+ - id: bandit
20
+ args: [-r, src]
21
+
22
+ - repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
23
+ rev: v9.21.0
24
+ hooks:
25
+ - id: commitlint
26
+ stages: [commit-msg]
27
+ additional_dependencies:
28
+ - "@commitlint/config-conventional"
@@ -0,0 +1,8 @@
1
+ - id: mailmap-check
2
+ name: Check .mailmap completeness
3
+ description: Detect unmapped Git identities by comparing .mailmap against the full commit history.
4
+ entry: mailmap-checker check
5
+ language: python
6
+ always_run: true
7
+ pass_filenames: false
8
+ stages: [pre-commit]
@@ -0,0 +1,17 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [0.1.0] - 2026-04-02
8
+
9
+ ### Added
10
+
11
+ - `check` command — scans Git history and exits non-zero when unmapped identities are found.
12
+ - `init` command — creates a `.mailmap` file and runs a full check.
13
+ - `fix` command — generates suggested `.mailmap` entries (`--dry-run` to preview).
14
+ - Identity grouping via Union-Find: same email (case-insensitive) and same email local-part across domains.
15
+ - Pre-commit hook integration (`mailmap-check`).
16
+
17
+ [0.1.0]: https://github.com/cansarigol/mailmap-checker/releases/tag/v0.1.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Can Sarigol
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,164 @@
1
+ Metadata-Version: 2.4
2
+ Name: mailmap-checker
3
+ Version: 0.1.0
4
+ Summary: Pre-commit hook that checks and maintains .mailmap completeness
5
+ Project-URL: Repository, https://github.com/cansarigol/mailmap-checker
6
+ Project-URL: Changelog, https://github.com/cansarigol/mailmap-checker/blob/main/CHANGELOG.md
7
+ Project-URL: Issues, https://github.com/cansarigol/mailmap-checker/issues
8
+ Author-email: Can Sarigol <ertugrulsarigol@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: deduplication,git,identity,mailmap,pre-commit
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Software Development :: Quality Assurance
23
+ Classifier: Topic :: Software Development :: Version Control :: Git
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/markdown
26
+
27
+ # mailmap-checker
28
+
29
+ [![PyPI version](https://img.shields.io/pypi/v/mailmap-checker)](https://pypi.org/project/mailmap-checker/)
30
+ [![Python versions](https://img.shields.io/pypi/pyversions/mailmap-checker)](https://pypi.org/project/mailmap-checker/)
31
+ [![CI](https://github.com/cansarigol/mailmap-checker/actions/workflows/ci.yml/badge.svg)](https://github.com/cansarigol/mailmap-checker/actions/workflows/ci.yml)
32
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
33
+
34
+ A pre-commit hook that detects unmapped Git identities by comparing your `.mailmap` against the full commit history. It groups authors by email address and email local-part so duplicates are caught even across domain changes.
35
+
36
+ ## How it works
37
+
38
+ The checker scans `git log` for all unique author identities and groups them using two rules:
39
+
40
+ **Rule 1 — Same email (case-insensitive)**
41
+
42
+ Identities that share the exact same email address are the same person.
43
+
44
+ ```
45
+ Alice Johnson <alice@acme.com>
46
+ alice.j <alice@acme.com> ← same email, grouped together
47
+ ```
48
+
49
+ **Rule 2 — Same email local-part (different domain)**
50
+
51
+ Identities whose email local-part (the part before `@`) matches are likely the same person who changed companies or used a different address.
52
+
53
+ ```
54
+ Alice Johnson <alice.johnson@acme.com>
55
+ Alice Johnson <alice.johnson@oldcorp.com> ← same local-part, grouped
56
+ Alice J <alice.johnson@personal.net> ← same local-part, grouped
57
+ ```
58
+
59
+ Once groups are built, the checker looks for identities that are **not mapped** in `.mailmap`. If a group has more than one identity and any of them is missing from the file, the hook fails and reports the gap.
60
+
61
+ ### Example
62
+
63
+ Given these three identities in git history and an empty `.mailmap`:
64
+
65
+ ```
66
+ Alice Johnson <alice.johnson@acme.com>
67
+ Alice Johnson <alice.johnson@oldcorp.com>
68
+ Alice J <alice.johnson@personal.net>
69
+ ```
70
+
71
+ `mailmap-checker check` detects the gap:
72
+
73
+ ```
74
+ Found 2 unmapped identities in 1 group:
75
+
76
+ Canonical: Alice J <alice.johnson@personal.net>
77
+ - Alice Johnson <alice.johnson@acme.com>
78
+ - Alice Johnson <alice.johnson@oldcorp.com>
79
+ ```
80
+
81
+ `mailmap-checker fix --dry-run` suggests entries to add:
82
+
83
+ ```
84
+ Suggested .mailmap entries:
85
+
86
+ Alice J <alice.johnson@personal.net> Alice Johnson <alice.johnson@acme.com>
87
+ Alice J <alice.johnson@personal.net> Alice Johnson <alice.johnson@oldcorp.com>
88
+ ```
89
+
90
+ > **Note:** When no `.mailmap` exists, the tool picks the alphabetically first identity as the canonical. In this case it chose `Alice J <alice.johnson@personal.net>`, but the actual preferred identity is likely `Alice Johnson <alice.johnson@acme.com>`. The `.mailmap` format places the canonical (left) and the alias (right) on each line. After running `fix`, open `.mailmap` and swap the canonical if needed:
91
+ >
92
+ > ```
93
+ > Alice Johnson <alice.johnson@acme.com> Alice Johnson <alice.johnson@oldcorp.com>
94
+ > Alice Johnson <alice.johnson@acme.com> Alice J <alice.johnson@personal.net>
95
+ > ```
96
+
97
+ ## Installation
98
+
99
+ ### Pre-commit hook (recommended)
100
+
101
+ ```yaml
102
+ # .pre-commit-config.yaml
103
+ repos:
104
+ - repo: https://github.com/cansarigol/mailmap-checker
105
+ rev: "" # run: pre-commit autoupdate
106
+ hooks:
107
+ - id: mailmap-check
108
+ ```
109
+
110
+ Then run `pre-commit autoupdate` to pin the latest release.
111
+
112
+ ### Standalone
113
+
114
+ ```bash
115
+ pip install mailmap-checker
116
+ ```
117
+
118
+ ## Usage
119
+
120
+ ### `check`
121
+
122
+ Scan all Git authors and exit non-zero if any identity is missing from `.mailmap`.
123
+
124
+ ```bash
125
+ mailmap-checker check
126
+ ```
127
+
128
+ ### `init`
129
+
130
+ Create a `.mailmap` file (if it does not exist) and run a full check.
131
+
132
+ ```bash
133
+ mailmap-checker init
134
+ ```
135
+
136
+ ### `fix`
137
+
138
+ Preview or apply suggested `.mailmap` entries.
139
+
140
+ ```bash
141
+ # Preview
142
+ mailmap-checker fix --dry-run
143
+
144
+ # Apply
145
+ mailmap-checker fix
146
+ ```
147
+
148
+ All commands accept `--mailmap <path>` to use a custom file path.
149
+
150
+ ## Contributing
151
+
152
+ ```bash
153
+ git clone https://github.com/cansarigol/mailmap-checker.git
154
+ cd mailmap-checker
155
+ uv sync
156
+ uv run poe setup # installs pre-commit and commit-msg hooks
157
+ uv run poe check # lint + security + tests
158
+ ```
159
+
160
+ Commits must follow [Conventional Commits](https://www.conventionalcommits.org/) with a required scope (e.g. `feat(cli): add --verbose flag`).
161
+
162
+ ## License
163
+
164
+ [MIT](LICENSE)
@@ -0,0 +1,138 @@
1
+ # mailmap-checker
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/mailmap-checker)](https://pypi.org/project/mailmap-checker/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/mailmap-checker)](https://pypi.org/project/mailmap-checker/)
5
+ [![CI](https://github.com/cansarigol/mailmap-checker/actions/workflows/ci.yml/badge.svg)](https://github.com/cansarigol/mailmap-checker/actions/workflows/ci.yml)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
+
8
+ A pre-commit hook that detects unmapped Git identities by comparing your `.mailmap` against the full commit history. It groups authors by email address and email local-part so duplicates are caught even across domain changes.
9
+
10
+ ## How it works
11
+
12
+ The checker scans `git log` for all unique author identities and groups them using two rules:
13
+
14
+ **Rule 1 — Same email (case-insensitive)**
15
+
16
+ Identities that share the exact same email address are the same person.
17
+
18
+ ```
19
+ Alice Johnson <alice@acme.com>
20
+ alice.j <alice@acme.com> ← same email, grouped together
21
+ ```
22
+
23
+ **Rule 2 — Same email local-part (different domain)**
24
+
25
+ Identities whose email local-part (the part before `@`) matches are likely the same person who changed companies or used a different address.
26
+
27
+ ```
28
+ Alice Johnson <alice.johnson@acme.com>
29
+ Alice Johnson <alice.johnson@oldcorp.com> ← same local-part, grouped
30
+ Alice J <alice.johnson@personal.net> ← same local-part, grouped
31
+ ```
32
+
33
+ Once groups are built, the checker looks for identities that are **not mapped** in `.mailmap`. If a group has more than one identity and any of them is missing from the file, the hook fails and reports the gap.
34
+
35
+ ### Example
36
+
37
+ Given these three identities in git history and an empty `.mailmap`:
38
+
39
+ ```
40
+ Alice Johnson <alice.johnson@acme.com>
41
+ Alice Johnson <alice.johnson@oldcorp.com>
42
+ Alice J <alice.johnson@personal.net>
43
+ ```
44
+
45
+ `mailmap-checker check` detects the gap:
46
+
47
+ ```
48
+ Found 2 unmapped identities in 1 group:
49
+
50
+ Canonical: Alice J <alice.johnson@personal.net>
51
+ - Alice Johnson <alice.johnson@acme.com>
52
+ - Alice Johnson <alice.johnson@oldcorp.com>
53
+ ```
54
+
55
+ `mailmap-checker fix --dry-run` suggests entries to add:
56
+
57
+ ```
58
+ Suggested .mailmap entries:
59
+
60
+ Alice J <alice.johnson@personal.net> Alice Johnson <alice.johnson@acme.com>
61
+ Alice J <alice.johnson@personal.net> Alice Johnson <alice.johnson@oldcorp.com>
62
+ ```
63
+
64
+ > **Note:** When no `.mailmap` exists, the tool picks the alphabetically first identity as the canonical. In this case it chose `Alice J <alice.johnson@personal.net>`, but the actual preferred identity is likely `Alice Johnson <alice.johnson@acme.com>`. The `.mailmap` format places the canonical (left) and the alias (right) on each line. After running `fix`, open `.mailmap` and swap the canonical if needed:
65
+ >
66
+ > ```
67
+ > Alice Johnson <alice.johnson@acme.com> Alice Johnson <alice.johnson@oldcorp.com>
68
+ > Alice Johnson <alice.johnson@acme.com> Alice J <alice.johnson@personal.net>
69
+ > ```
70
+
71
+ ## Installation
72
+
73
+ ### Pre-commit hook (recommended)
74
+
75
+ ```yaml
76
+ # .pre-commit-config.yaml
77
+ repos:
78
+ - repo: https://github.com/cansarigol/mailmap-checker
79
+ rev: "" # run: pre-commit autoupdate
80
+ hooks:
81
+ - id: mailmap-check
82
+ ```
83
+
84
+ Then run `pre-commit autoupdate` to pin the latest release.
85
+
86
+ ### Standalone
87
+
88
+ ```bash
89
+ pip install mailmap-checker
90
+ ```
91
+
92
+ ## Usage
93
+
94
+ ### `check`
95
+
96
+ Scan all Git authors and exit non-zero if any identity is missing from `.mailmap`.
97
+
98
+ ```bash
99
+ mailmap-checker check
100
+ ```
101
+
102
+ ### `init`
103
+
104
+ Create a `.mailmap` file (if it does not exist) and run a full check.
105
+
106
+ ```bash
107
+ mailmap-checker init
108
+ ```
109
+
110
+ ### `fix`
111
+
112
+ Preview or apply suggested `.mailmap` entries.
113
+
114
+ ```bash
115
+ # Preview
116
+ mailmap-checker fix --dry-run
117
+
118
+ # Apply
119
+ mailmap-checker fix
120
+ ```
121
+
122
+ All commands accept `--mailmap <path>` to use a custom file path.
123
+
124
+ ## Contributing
125
+
126
+ ```bash
127
+ git clone https://github.com/cansarigol/mailmap-checker.git
128
+ cd mailmap-checker
129
+ uv sync
130
+ uv run poe setup # installs pre-commit and commit-msg hooks
131
+ uv run poe check # lint + security + tests
132
+ ```
133
+
134
+ Commits must follow [Conventional Commits](https://www.conventionalcommits.org/) with a required scope (e.g. `feat(cli): add --verbose flag`).
135
+
136
+ ## License
137
+
138
+ [MIT](LICENSE)
@@ -0,0 +1,11 @@
1
+ module.exports = {
2
+ extends: ['@commitlint/config-conventional'],
3
+ rules: {
4
+ 'scope-empty': [2, 'never'],
5
+ 'type-enum': [
6
+ 2,
7
+ 'always',
8
+ ['feat', 'fix', 'docs', 'perf', 'test', 'build', 'ci', 'chore', 'revert', 'sec', 'refactor'],
9
+ ],
10
+ },
11
+ };
@@ -0,0 +1,117 @@
1
+ [project]
2
+ name = "mailmap-checker"
3
+ version = "0.1.0"
4
+ description = "Pre-commit hook that checks and maintains .mailmap completeness"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ requires-python = ">=3.10"
8
+ authors = [{ name = "Can Sarigol", email = "ertugrulsarigol@gmail.com" }]
9
+ classifiers = [
10
+ "Development Status :: 4 - Beta",
11
+ "Environment :: Console",
12
+ "Intended Audience :: Developers",
13
+ "License :: OSI Approved :: MIT License",
14
+ "Operating System :: OS Independent",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.10",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Topic :: Software Development :: Quality Assurance",
21
+ "Topic :: Software Development :: Version Control :: Git",
22
+ ]
23
+ keywords = ["git", "mailmap", "pre-commit", "identity", "deduplication"]
24
+
25
+ [project.scripts]
26
+ mailmap-checker = "mailmap_checker.cli:main"
27
+
28
+ [project.urls]
29
+ Repository = "https://github.com/cansarigol/mailmap-checker"
30
+ Changelog = "https://github.com/cansarigol/mailmap-checker/blob/main/CHANGELOG.md"
31
+ Issues = "https://github.com/cansarigol/mailmap-checker/issues"
32
+
33
+ [build-system]
34
+ requires = ["hatchling"]
35
+ build-backend = "hatchling.build"
36
+
37
+ [tool.hatch.build.targets.wheel]
38
+ packages = ["src/mailmap_checker"]
39
+
40
+ [dependency-groups]
41
+ dev = [
42
+ "pytest>=8.0",
43
+ "pytest-cov>=6.0",
44
+ "ruff>=0.11",
45
+ "bandit>=1.8",
46
+ "poethepoet>=0.34",
47
+ "pre-commit>=4.0",
48
+ ]
49
+
50
+ [tool.ruff]
51
+ line-length = 88
52
+ target-version = "py310"
53
+ src = ["src", "tests"]
54
+
55
+ [tool.ruff.lint]
56
+ select = [
57
+ "A",
58
+ "E",
59
+ "W",
60
+ "F",
61
+ "I",
62
+ "B",
63
+ "C4",
64
+ "PIE",
65
+ "FURB",
66
+ "PERF",
67
+ ]
68
+
69
+ [tool.pytest.ini_options]
70
+ testpaths = ["tests"]
71
+
72
+ [tool.coverage.run]
73
+ source = ["mailmap_checker"]
74
+
75
+ [tool.coverage.report]
76
+ show_missing = true
77
+ fail_under = 100
78
+
79
+ [tool.bandit]
80
+ targets = ["src"]
81
+
82
+ [tool.poe]
83
+ env = { UV_PROJECT_ENVIRONMENT = "venv" }
84
+
85
+ [tool.poe.tasks.setup]
86
+ help = "Install git hooks (run once after cloning)"
87
+ sequence = [
88
+ { cmd = "pre-commit install" },
89
+ { cmd = "pre-commit install --hook-type commit-msg" },
90
+ ]
91
+
92
+ [tool.poe.tasks.lint]
93
+ help = "Run ruff linter and format check"
94
+ sequence = [
95
+ { cmd = "ruff check src tests" },
96
+ { cmd = "ruff format --check src tests" },
97
+ ]
98
+
99
+ [tool.poe.tasks.format]
100
+ help = "Format code with ruff"
101
+ cmd = "ruff format src tests"
102
+
103
+ [tool.poe.tasks.test]
104
+ help = "Run tests with coverage"
105
+ cmd = "pytest --cov --cov-report=term-missing"
106
+
107
+ [tool.poe.tasks.security]
108
+ help = "Run bandit security checks"
109
+ cmd = "bandit -r src"
110
+
111
+ [tool.poe.tasks.check]
112
+ help = "Run all checks (lint + security + test)"
113
+ sequence = [
114
+ { ref = "lint" },
115
+ { ref = "security" },
116
+ { ref = "test" },
117
+ ]
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"