git-history-extraction 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,259 @@
1
+ Metadata-Version: 2.3
2
+ Name: git-history-extraction
3
+ Version: 0.1.0
4
+ Summary: Extract and analyze git commit history with support for filtering by trailers, date ranges, and AI-powered summarization for changelogs
5
+ Keywords: git,changelog,history,commit,trailers,ai,summarization,openai,gemini
6
+ Author: Michael Bianco
7
+ Author-email: Michael Bianco <mike@mikebian.co>
8
+ Requires-Dist: click>=8.0
9
+ Requires-Python: >=3.10
10
+ Project-URL: Repository, https://github.com/iloveitaly/git-history-extraction
11
+ Description-Content-Type: text/markdown
12
+
13
+ # git-history-extraction
14
+
15
+ A tool to extract and filter git commit history, making it easy to pipe to AI tools for changelog generation and summaries.
16
+
17
+ ## Features
18
+
19
+ - Extract git commits with metadata (SHA, date, files, message)
20
+ - Filter commits by time range or starting commit
21
+ - Extract and filter git trailers (e.g., `Co-authored-by`, `User-Facing`)
22
+ - Output in simple text or JSON format
23
+ - Pipe output to AI tools (OpenAI, Gemini, Claude) for automated summarization
24
+
25
+ ## Installation
26
+
27
+ ### Using uv (Recommended)
28
+
29
+ No installation needed! The tool can be run directly:
30
+
31
+ ```bash
32
+ uv run git-history-extraction --help
33
+ ```
34
+
35
+ ### Install as Package
36
+
37
+ ```bash
38
+ pip install git-history-extraction
39
+ ```
40
+
41
+ ## Usage
42
+
43
+ ### Basic Examples
44
+
45
+ Extract commits from the last 24 hours (default):
46
+ ```bash
47
+ git-history-extraction
48
+ ```
49
+
50
+ Extract commits from the last 7 days:
51
+ ```bash
52
+ git-history-extraction --since "7 days ago"
53
+ ```
54
+
55
+ Extract commits from a specific repository:
56
+ ```bash
57
+ git-history-extraction --repo /path/to/repo --since "1 week ago"
58
+ ```
59
+
60
+ ### Output Formats
61
+
62
+ Simple text format (default):
63
+ ```bash
64
+ git-history-extraction --since "1 day ago"
65
+ ```
66
+
67
+ JSON format for piping to other tools:
68
+ ```bash
69
+ git-history-extraction --since "1 day ago" --format json
70
+ ```
71
+
72
+ ### Commit Range Selection
73
+
74
+ By time range:
75
+ ```bash
76
+ git-history-extraction --since "2024-01-01"
77
+ ```
78
+
79
+ From a specific commit to HEAD:
80
+ ```bash
81
+ git-history-extraction --since-commit abc1234
82
+ ```
83
+
84
+ ### Git Trailers
85
+
86
+ Extract specific trailers only (case-insensitive):
87
+ ```bash
88
+ git-history-extraction --since "1 week ago" --trailers "co-authored-by,reviewed-by"
89
+ ```
90
+
91
+ ## Piping to AI Tools for Summarization
92
+
93
+ This tool extracts and formats git history, making it easy to pipe to AI tools for summarization. The tool itself **does not perform AI summarization**—it prepares the data so you can use your preferred AI tool.
94
+
95
+ The tool enables you to extract targeted slices of git history for different audiences. For example, use git trailers like `User-Facing:` to mark end-user changes, then extract and pipe them to AI for changelogs or internal notifications.
96
+
97
+ ### Creating Structured Git Trailers with AI
98
+
99
+ Combine with [aiautocommit](https://github.com/iloveitaly/aiautocommit) to automatically generate git trailers during commits. This creates a structured history that can be easily filtered and summarized for different audiences.
100
+
101
+ **Example custom commit prompt for aiautocommit:**
102
+
103
+ ```markdown
104
+ # IMPORTANT: Your Instructions
105
+
106
+ You are an expert software developer. Generate a commit message from the `git diff` output below using these rules:
107
+
108
+ ## 1. Subject Line
109
+
110
+ - Use a conventional commit prefix:
111
+ - `feat`: New features
112
+ - `fix`: Bug fixes, including user-visible design or style fixes.
113
+ - `docs`: Changes only to internal documentation (e.g., `.md`, `.rst`) or code comments.
114
+ - `style`: Formatting, linting, or code style changes in code files.
115
+ - `refactor`: Code structure improvements (no behavior changes).
116
+ - `build`: Updates to build scripts or configs (e.g., `Makefile`, `Justfile`, `Dockerfile`, `package.json`).
117
+ - `deploy`: Deployment script or IAC updates.
118
+ - `test`: Changes to tests
119
+ - Add optional scope in parentheses when changes affect a specific module (e.g., `feat(auth): add login`)
120
+ - Limit to 50 characters after the prefix and scope.
121
+ - Use imperative mood (e.g., "improve layout").
122
+ - Describe the intent or outcome (e.g., "prevent text overflow" not "add break-all").
123
+ - Be specific about the change ("validate email format" not "improve validation").
124
+
125
+ ## 2. Extended Commit Message
126
+
127
+ - Include only if changes have non-obvious implications, fix complex bugs, or introduce breaking changes.
128
+ - Separate from subject with one blank line.
129
+ - Use markdown bullets focusing on **why** the change was needed and **what impact** it has.
130
+ - Mention side effects, user impact, or important trade-offs.
131
+
132
+ ## 3. User-facing Changes
133
+
134
+ If the change is something that a end-user (not internal admin!) would see, include a `User-facing:` git trailer with a sentence
135
+ or two explaining, to the user, what they would see differently because of this change.
136
+
137
+ ## 4. General Guidelines
138
+
139
+ - Prioritize the purpose of changes over listing tools or properties used.
140
+ - Keep concise; avoid obvious or verbose details.
141
+ - Always generate a message based on the diff, even with limited context.
142
+
143
+ ## 5. Scopes
144
+
145
+ Optional scopes (e.g., `feat(api):`):
146
+
147
+ - `match`: frontend or backend changes tied
148
+ - `site`: content, additional pages, etc for the static site content
149
+ - `internal-admin`: internal admin changes (including CMS)
150
+ ```
151
+
152
+ With this setup, commits automatically get `User-facing:` trailers. You can then extract and summarize them:
153
+
154
+ ```bash
155
+ # Extract only user-facing changes from the last sprint
156
+ git-history-extraction --since "last monday" --trailers "User-facing" | \
157
+ gemini -i "Create a user-friendly changelog from these changes"
158
+ ```
159
+
160
+ ### Using with Gemini CLI
161
+
162
+ Extract user-facing changes and generate a non-technical summary:
163
+ ```bash
164
+ git-history-extraction --repo . --since "last monday" \
165
+ --trailers "User-Facing" | \
166
+ gemini -i "This is a compressed git history identifying user-facing changes. \
167
+ Can you write a 1-2 sentence overview of the changes, with a list of bullets \
168
+ identifying changes. This is for a non-technical internal audience, letting \
169
+ them know what the development team has done. Separate into 'new' and 'fixed' \
170
+ sections. Include a 'Updates Since' with the date of the first commit in the \
171
+ history. Remove fluff, keep it concise and information dense."
172
+ ```
173
+
174
+ ### Using the OpenAI Playground Script (Optional)
175
+
176
+ An optional playground script is included that demonstrates OpenAI integration:
177
+
178
+ ```bash
179
+ # Generate summary with OpenAI
180
+ git-history-extraction --since "1 week ago" --format json | \
181
+ uv run playground/summarize_commits.py
182
+
183
+ # Preview the prompt without calling OpenAI
184
+ git-history-extraction --since "1 week ago" --format json | \
185
+ uv run playground/summarize_commits.py --dump-prompt
186
+ ```
187
+
188
+ **Requirements:**
189
+ - `OPENAI_API_KEY` environment variable
190
+ - The script uses GPT-4o-mini by default
191
+
192
+ This is just an example—you can pipe to any AI tool you prefer. See [playground/README.md](playground/README.md) for more details.
193
+
194
+ ## Output Format
195
+
196
+ ### Simple Format
197
+
198
+ Each commit is displayed with:
199
+ - **Commit:** SHA hash
200
+ - **Date:** ISO 8601 timestamp
201
+ - **Files:** Comma-separated list of changed files
202
+ - **Message:** Commit body with trailers removed
203
+
204
+ ### JSON Format
205
+
206
+ Array of commit objects:
207
+ ```json
208
+ [
209
+ {
210
+ "sha": "abc123...",
211
+ "date": "2024-10-31T08:00:00-06:00",
212
+ "body": "commit message with trailers",
213
+ "files": ["file1.py", "file2.md"]
214
+ }
215
+ ]
216
+ ```
217
+
218
+ ## Options
219
+
220
+ | Option | Description | Default |
221
+ |--------|-------------|---------|
222
+ | `--since TEXT` | ISO date/time or relative time | `"24 hours ago"` |
223
+ | `--since-commit TEXT` | Start from specific commit (overrides `--since`) | None |
224
+ | `--repo DIRECTORY` | Path to git repository | `.` (current directory) |
225
+ | `--trailers TEXT` | Comma-separated trailer keys to extract | None (show all) |
226
+ | `--format [simple\|json]` | Output format | `simple` |
227
+
228
+ ## How It Works
229
+
230
+ - Uses `git log` with custom formatting for efficient single-pass extraction
231
+ - Parses commit metadata, body, and file changes in one command
232
+ - Intelligently extracts git trailers from commit messages
233
+ - No per-commit subprocess calls for optimal performance
234
+
235
+ ## Development
236
+
237
+ The main logic lives in `git_history_extraction/__init__.py`. The tool is structured as a Python package with:
238
+
239
+ - **main.py:** Entry point script
240
+ - **git_history_extraction/:** Core module with extraction logic
241
+ - **playground/:** Optional AI summarization scripts
242
+
243
+ ### Running Tests
244
+
245
+ ```bash
246
+ pytest
247
+ ```
248
+
249
+ ## Limitations
250
+
251
+ - Large commit ranges may generate significant output; consider narrowing the time range
252
+ - This tool extracts and formats data only—AI summarization requires external tools
253
+ - Git must be available in PATH
254
+
255
+ ## Requirements
256
+
257
+ - Python >= 3.9
258
+ - git
259
+ - [uv](https://docs.astral.sh/uv/) (recommended) or pip
@@ -0,0 +1,247 @@
1
+ # git-history-extraction
2
+
3
+ A tool to extract and filter git commit history, making it easy to pipe to AI tools for changelog generation and summaries.
4
+
5
+ ## Features
6
+
7
+ - Extract git commits with metadata (SHA, date, files, message)
8
+ - Filter commits by time range or starting commit
9
+ - Extract and filter git trailers (e.g., `Co-authored-by`, `User-Facing`)
10
+ - Output in simple text or JSON format
11
+ - Pipe output to AI tools (OpenAI, Gemini, Claude) for automated summarization
12
+
13
+ ## Installation
14
+
15
+ ### Using uv (Recommended)
16
+
17
+ No installation needed! The tool can be run directly:
18
+
19
+ ```bash
20
+ uv run git-history-extraction --help
21
+ ```
22
+
23
+ ### Install as Package
24
+
25
+ ```bash
26
+ pip install git-history-extraction
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ### Basic Examples
32
+
33
+ Extract commits from the last 24 hours (default):
34
+ ```bash
35
+ git-history-extraction
36
+ ```
37
+
38
+ Extract commits from the last 7 days:
39
+ ```bash
40
+ git-history-extraction --since "7 days ago"
41
+ ```
42
+
43
+ Extract commits from a specific repository:
44
+ ```bash
45
+ git-history-extraction --repo /path/to/repo --since "1 week ago"
46
+ ```
47
+
48
+ ### Output Formats
49
+
50
+ Simple text format (default):
51
+ ```bash
52
+ git-history-extraction --since "1 day ago"
53
+ ```
54
+
55
+ JSON format for piping to other tools:
56
+ ```bash
57
+ git-history-extraction --since "1 day ago" --format json
58
+ ```
59
+
60
+ ### Commit Range Selection
61
+
62
+ By time range:
63
+ ```bash
64
+ git-history-extraction --since "2024-01-01"
65
+ ```
66
+
67
+ From a specific commit to HEAD:
68
+ ```bash
69
+ git-history-extraction --since-commit abc1234
70
+ ```
71
+
72
+ ### Git Trailers
73
+
74
+ Extract specific trailers only (case-insensitive):
75
+ ```bash
76
+ git-history-extraction --since "1 week ago" --trailers "co-authored-by,reviewed-by"
77
+ ```
78
+
79
+ ## Piping to AI Tools for Summarization
80
+
81
+ This tool extracts and formats git history, making it easy to pipe to AI tools for summarization. The tool itself **does not perform AI summarization**—it prepares the data so you can use your preferred AI tool.
82
+
83
+ The tool enables you to extract targeted slices of git history for different audiences. For example, use git trailers like `User-Facing:` to mark end-user changes, then extract and pipe them to AI for changelogs or internal notifications.
84
+
85
+ ### Creating Structured Git Trailers with AI
86
+
87
+ Combine with [aiautocommit](https://github.com/iloveitaly/aiautocommit) to automatically generate git trailers during commits. This creates a structured history that can be easily filtered and summarized for different audiences.
88
+
89
+ **Example custom commit prompt for aiautocommit:**
90
+
91
+ ```markdown
92
+ # IMPORTANT: Your Instructions
93
+
94
+ You are an expert software developer. Generate a commit message from the `git diff` output below using these rules:
95
+
96
+ ## 1. Subject Line
97
+
98
+ - Use a conventional commit prefix:
99
+ - `feat`: New features
100
+ - `fix`: Bug fixes, including user-visible design or style fixes.
101
+ - `docs`: Changes only to internal documentation (e.g., `.md`, `.rst`) or code comments.
102
+ - `style`: Formatting, linting, or code style changes in code files.
103
+ - `refactor`: Code structure improvements (no behavior changes).
104
+ - `build`: Updates to build scripts or configs (e.g., `Makefile`, `Justfile`, `Dockerfile`, `package.json`).
105
+ - `deploy`: Deployment script or IAC updates.
106
+ - `test`: Changes to tests
107
+ - Add optional scope in parentheses when changes affect a specific module (e.g., `feat(auth): add login`)
108
+ - Limit to 50 characters after the prefix and scope.
109
+ - Use imperative mood (e.g., "improve layout").
110
+ - Describe the intent or outcome (e.g., "prevent text overflow" not "add break-all").
111
+ - Be specific about the change ("validate email format" not "improve validation").
112
+
113
+ ## 2. Extended Commit Message
114
+
115
+ - Include only if changes have non-obvious implications, fix complex bugs, or introduce breaking changes.
116
+ - Separate from subject with one blank line.
117
+ - Use markdown bullets focusing on **why** the change was needed and **what impact** it has.
118
+ - Mention side effects, user impact, or important trade-offs.
119
+
120
+ ## 3. User-facing Changes
121
+
122
+ If the change is something that a end-user (not internal admin!) would see, include a `User-facing:` git trailer with a sentence
123
+ or two explaining, to the user, what they would see differently because of this change.
124
+
125
+ ## 4. General Guidelines
126
+
127
+ - Prioritize the purpose of changes over listing tools or properties used.
128
+ - Keep concise; avoid obvious or verbose details.
129
+ - Always generate a message based on the diff, even with limited context.
130
+
131
+ ## 5. Scopes
132
+
133
+ Optional scopes (e.g., `feat(api):`):
134
+
135
+ - `match`: frontend or backend changes tied
136
+ - `site`: content, additional pages, etc for the static site content
137
+ - `internal-admin`: internal admin changes (including CMS)
138
+ ```
139
+
140
+ With this setup, commits automatically get `User-facing:` trailers. You can then extract and summarize them:
141
+
142
+ ```bash
143
+ # Extract only user-facing changes from the last sprint
144
+ git-history-extraction --since "last monday" --trailers "User-facing" | \
145
+ gemini -i "Create a user-friendly changelog from these changes"
146
+ ```
147
+
148
+ ### Using with Gemini CLI
149
+
150
+ Extract user-facing changes and generate a non-technical summary:
151
+ ```bash
152
+ git-history-extraction --repo . --since "last monday" \
153
+ --trailers "User-Facing" | \
154
+ gemini -i "This is a compressed git history identifying user-facing changes. \
155
+ Can you write a 1-2 sentence overview of the changes, with a list of bullets \
156
+ identifying changes. This is for a non-technical internal audience, letting \
157
+ them know what the development team has done. Separate into 'new' and 'fixed' \
158
+ sections. Include a 'Updates Since' with the date of the first commit in the \
159
+ history. Remove fluff, keep it concise and information dense."
160
+ ```
161
+
162
+ ### Using the OpenAI Playground Script (Optional)
163
+
164
+ An optional playground script is included that demonstrates OpenAI integration:
165
+
166
+ ```bash
167
+ # Generate summary with OpenAI
168
+ git-history-extraction --since "1 week ago" --format json | \
169
+ uv run playground/summarize_commits.py
170
+
171
+ # Preview the prompt without calling OpenAI
172
+ git-history-extraction --since "1 week ago" --format json | \
173
+ uv run playground/summarize_commits.py --dump-prompt
174
+ ```
175
+
176
+ **Requirements:**
177
+ - `OPENAI_API_KEY` environment variable
178
+ - The script uses GPT-4o-mini by default
179
+
180
+ This is just an example—you can pipe to any AI tool you prefer. See [playground/README.md](playground/README.md) for more details.
181
+
182
+ ## Output Format
183
+
184
+ ### Simple Format
185
+
186
+ Each commit is displayed with:
187
+ - **Commit:** SHA hash
188
+ - **Date:** ISO 8601 timestamp
189
+ - **Files:** Comma-separated list of changed files
190
+ - **Message:** Commit body with trailers removed
191
+
192
+ ### JSON Format
193
+
194
+ Array of commit objects:
195
+ ```json
196
+ [
197
+ {
198
+ "sha": "abc123...",
199
+ "date": "2024-10-31T08:00:00-06:00",
200
+ "body": "commit message with trailers",
201
+ "files": ["file1.py", "file2.md"]
202
+ }
203
+ ]
204
+ ```
205
+
206
+ ## Options
207
+
208
+ | Option | Description | Default |
209
+ |--------|-------------|---------|
210
+ | `--since TEXT` | ISO date/time or relative time | `"24 hours ago"` |
211
+ | `--since-commit TEXT` | Start from specific commit (overrides `--since`) | None |
212
+ | `--repo DIRECTORY` | Path to git repository | `.` (current directory) |
213
+ | `--trailers TEXT` | Comma-separated trailer keys to extract | None (show all) |
214
+ | `--format [simple\|json]` | Output format | `simple` |
215
+
216
+ ## How It Works
217
+
218
+ - Uses `git log` with custom formatting for efficient single-pass extraction
219
+ - Parses commit metadata, body, and file changes in one command
220
+ - Intelligently extracts git trailers from commit messages
221
+ - No per-commit subprocess calls for optimal performance
222
+
223
+ ## Development
224
+
225
+ The main logic lives in `git_history_extraction/__init__.py`. The tool is structured as a Python package with:
226
+
227
+ - **main.py:** Entry point script
228
+ - **git_history_extraction/:** Core module with extraction logic
229
+ - **playground/:** Optional AI summarization scripts
230
+
231
+ ### Running Tests
232
+
233
+ ```bash
234
+ pytest
235
+ ```
236
+
237
+ ## Limitations
238
+
239
+ - Large commit ranges may generate significant output; consider narrowing the time range
240
+ - This tool extracts and formats data only—AI summarization requires external tools
241
+ - Git must be available in PATH
242
+
243
+ ## Requirements
244
+
245
+ - Python >= 3.9
246
+ - git
247
+ - [uv](https://docs.astral.sh/uv/) (recommended) or pip
@@ -0,0 +1,189 @@
1
+ import subprocess
2
+ import re
3
+ from pathlib import Path
4
+ import click
5
+
6
+
7
+ def get_commit_files(sha: str, repo_path: Path | None = None) -> list[str]:
8
+ cmd = ["git", "diff-tree", "--no-commit-id", "--name-only", "-r", sha]
9
+ result = subprocess.run(
10
+ cmd,
11
+ capture_output=True,
12
+ text=True,
13
+ check=True,
14
+ cwd=str(repo_path) if repo_path else None,
15
+ )
16
+ return [l.strip() for l in result.stdout.splitlines() if l.strip()]
17
+
18
+
19
+ def get_git_commits(since, since_commit=None, repo_path: Path | None = None):
20
+ rec_sep = "\x1e"
21
+ fld_sep = "\x1f"
22
+ end_hdr = "\x1d"
23
+
24
+ pretty = f"{rec_sep}%H{fld_sep}%cI{fld_sep}%B{end_hdr}"
25
+ if since_commit:
26
+ cmd = [
27
+ "git",
28
+ "log",
29
+ f"{since_commit}..HEAD",
30
+ f"--pretty=format:{pretty}",
31
+ "--name-only",
32
+ ]
33
+ else:
34
+ cmd = [
35
+ "git",
36
+ "log",
37
+ f"--since={since}",
38
+ f"--pretty=format:{pretty}",
39
+ "--name-only",
40
+ ]
41
+
42
+ result = subprocess.run(
43
+ cmd,
44
+ capture_output=True,
45
+ text=True,
46
+ check=True,
47
+ cwd=str(repo_path) if repo_path else None,
48
+ )
49
+
50
+ commits: list[dict] = []
51
+ stream = result.stdout
52
+ for block in stream.split(rec_sep):
53
+ if not block.strip():
54
+ continue
55
+ if end_hdr not in block:
56
+ continue
57
+ header, files_blob = block.split(end_hdr, 1)
58
+ parts = header.split(fld_sep, 2)
59
+ if len(parts) != 3:
60
+ continue
61
+ sha, date_iso, body = parts
62
+ files = [l.strip() for l in files_blob.splitlines() if l.strip()]
63
+ commits.append(
64
+ {
65
+ "sha": sha.strip(),
66
+ "date": date_iso.strip(),
67
+ "body": body.strip(),
68
+ "files": files,
69
+ }
70
+ )
71
+
72
+ return commits
73
+
74
+
75
+ def remove_git_trailers(commit_body: str) -> str:
76
+ lines = commit_body.splitlines()
77
+ trailer_regex = re.compile(r"^\s*[-*]?\s*[^:]+:\s*.*$")
78
+
79
+ while lines and not lines[-1].strip():
80
+ lines.pop()
81
+
82
+ while lines and trailer_regex.match(lines[-1]):
83
+ lines.pop()
84
+
85
+ while lines and not lines[-1].strip():
86
+ lines.pop()
87
+
88
+ return "\n".join(lines)
89
+
90
+
91
+ def extract_git_trailers(commit_body: str) -> list[tuple[str, str]]:
92
+ lines = commit_body.splitlines()
93
+ trailer_regex = re.compile(r"^\s*[-*]?\s*([^:]+):\s*(.*)$")
94
+
95
+ idx = len(lines) - 1
96
+ while idx >= 0 and not lines[idx].strip():
97
+ idx -= 1
98
+
99
+ collected: list[tuple[str, str]] = []
100
+ seen_keys: set[tuple[str, str]] = set()
101
+ j = idx
102
+ while j >= 0:
103
+ m = trailer_regex.match(lines[j])
104
+ if not m:
105
+ break
106
+ pair = (m.group(1), m.group(2))
107
+ collected.append(pair)
108
+ seen_keys.add((m.group(1).strip().lower(), m.group(2).strip()))
109
+ j -= 1
110
+ collected.reverse()
111
+
112
+ for line in lines:
113
+ m = trailer_regex.match(line)
114
+ if not m:
115
+ continue
116
+ key_norm = (m.group(1).strip().lower(), m.group(2).strip())
117
+ if key_norm in seen_keys:
118
+ continue
119
+ collected.append((m.group(1), m.group(2)))
120
+ seen_keys.add(key_norm)
121
+
122
+ return collected
123
+
124
+
125
+ @click.command()
126
+ @click.option(
127
+ "--since",
128
+ type=str,
129
+ default="24 hours ago",
130
+ help="ISO date/time or relative time (default: '24 hours ago')",
131
+ )
132
+ @click.option(
133
+ "--since-commit",
134
+ type=str,
135
+ default=None,
136
+ help="Specific commit sha to start from (e.g. abc123). Overrides --since if provided.",
137
+ )
138
+ @click.option(
139
+ "--repo",
140
+ type=click.Path(exists=True, file_okay=False, dir_okay=True, path_type=Path),
141
+ default=Path("."),
142
+ help="Path to the git repository to summarize.",
143
+ )
144
+ @click.option(
145
+ "--trailers",
146
+ type=str,
147
+ default=None,
148
+ help="Comma-separated trailer key(s) to output (case-insensitive).",
149
+ )
150
+ @click.option(
151
+ "--format",
152
+ type=click.Choice(["simple", "json"]),
153
+ default="simple",
154
+ help="Output format (default: simple)",
155
+ )
156
+ def main(since: str, since_commit: str | None, repo: Path, trailers: str | None, format: str):
157
+ commits = get_git_commits(since, since_commit, repo_path=repo)
158
+ if not commits:
159
+ click.echo("No commits found using the specified parameters.")
160
+ return
161
+
162
+ if trailers is not None:
163
+ selectors = {part.strip().lower() for part in trailers.split(",") if part.strip()}
164
+ out_lines: list[str] = []
165
+ for c in commits:
166
+ trailer_items = extract_git_trailers(c["body"]) or []
167
+ if selectors:
168
+ trailer_items = [t for t in trailer_items if t[0].lower() in selectors]
169
+ if not trailer_items:
170
+ continue
171
+ files = ", ".join(c.get("files", []))
172
+ out_lines.append(f"Commit: {c['sha']}\nDate: {c['date']}\nFiles: {files}")
173
+ out_lines.extend([f"{k}: {v}" for k, v in trailer_items])
174
+ out_lines.append("")
175
+ click.echo("\n".join(out_lines).rstrip())
176
+ return
177
+
178
+ if format == "json":
179
+ import json
180
+ click.echo(json.dumps(commits, indent=2))
181
+ else:
182
+ for c in commits:
183
+ body = remove_git_trailers(c["body"]) or "(no message)"
184
+ files = ", ".join(c.get("files", []))
185
+ click.echo(f"Commit: {c['sha']}")
186
+ click.echo(f"Date: {c['date']}")
187
+ click.echo(f"Files: {files}")
188
+ click.echo(f"\n{body}\n")
189
+ click.echo("-" * 80)
@@ -0,0 +1,26 @@
1
+ [project]
2
+ name = "git-history-extraction"
3
+ version = "0.1.0"
4
+ description = "Extract and analyze git commit history with support for filtering by trailers, date ranges, and AI-powered summarization for changelogs"
5
+ keywords = ["git", "changelog", "history", "commit", "trailers", "ai", "summarization", "openai", "gemini"]
6
+ readme = "README.md"
7
+ requires-python = ">=3.10"
8
+ dependencies = ["click>=8.0"]
9
+ authors = [{ name = "Michael Bianco", email = "mike@mikebian.co" }]
10
+ urls = { "Repository" = "https://github.com/iloveitaly/git-history-extraction" }
11
+
12
+ # additional packaging information: https://packaging.python.org/en/latest/specifications/core-metadata/#license
13
+ [project.scripts]
14
+ git-history-extraction = "git_history_extraction:main"
15
+
16
+ [build-system]
17
+ requires = ["uv_build>=0.8.11,<0.9.0"]
18
+ build-backend = "uv_build"
19
+
20
+ [tool.uv.build-backend]
21
+ # avoids the src/ directory structure
22
+ module-root = ""
23
+
24
+ [dependency-groups]
25
+ dev = ["pytest>=8.3.3"]
26
+