0
# Git Operations
1
2
Extensive git integration including commit creation, tag management, branch operations, repository status checking, and smart file handling with EOL support. Commitizen provides comprehensive git operations that handle edge cases and provide robust integration with git workflows.
3
4
## Capabilities
5
6
### Git Object Types
7
8
Core data structures representing git objects with metadata.
9
10
```python { .api }
11
class GitObject:
12
"""Base class for git objects."""
13
14
def __init__(self, rev: str):
15
"""
16
Initialize git object.
17
18
Parameters:
19
- rev: Git revision identifier
20
"""
21
22
class GitCommit(NamedTuple):
23
"""Git commit representation with metadata."""
24
rev: str # Commit SHA hash
25
title: str # Commit title (first line)
26
body: str # Commit body (remaining lines)
27
author: str # Author name
28
author_email: str # Author email
29
date: str # Commit date (ISO format)
30
31
class GitTag(NamedTuple):
32
"""Git tag representation with metadata."""
33
name: str # Tag name
34
rev: str # Tagged commit SHA
35
date: str # Tag creation date
36
```
37
38
### End-of-Line Handling
39
40
Support for different end-of-line formats in text files.
41
42
```python { .api }
43
class EOLTypes(Enum):
44
"""End-of-line type enumeration."""
45
LF = "lf" # Unix line endings (\n)
46
CRLF = "crlf" # Windows line endings (\r\n)
47
NATIVE = "native" # Platform-specific line endings
48
```
49
50
### Repository Operations
51
52
#### Repository Status and Validation
53
54
Check repository state and validate git project status.
55
56
```python { .api }
57
def is_git_project(path: str = ".") -> bool:
58
"""
59
Check if directory is a git repository.
60
61
Parameters:
62
- path: Directory path to check
63
64
Returns:
65
True if directory contains a git repository
66
"""
67
68
def find_git_project_root(path: str = ".") -> Path:
69
"""
70
Find the root directory of the git repository.
71
72
Searches upward from the given path to find .git directory.
73
74
Parameters:
75
- path: Starting directory path
76
77
Returns:
78
Path to git repository root
79
80
Raises:
81
NotAGitProjectError: If not in a git repository
82
"""
83
84
def is_staging_clean(check_untracked_files: bool = False) -> bool:
85
"""
86
Check if git staging area is clean.
87
88
Parameters:
89
- check_untracked_files: Include untracked files in check
90
91
Returns:
92
True if staging area has no changes
93
"""
94
```
95
96
### Basic Git Commands
97
98
#### File Operations
99
100
Add files to staging area and manage working directory.
101
102
```python { .api }
103
def add(pathspecs: list[str]) -> Command:
104
"""
105
Add files to git staging area.
106
107
Parameters:
108
- pathspecs: List of file paths or patterns to add
109
110
Returns:
111
Command execution result
112
113
Raises:
114
GitCommandError: If git add command fails
115
"""
116
```
117
118
#### Commit Operations
119
120
Create git commits with various options and validation.
121
122
```python { .api }
123
def commit(message: str, args: str = "") -> Command:
124
"""
125
Create git commit with message.
126
127
Parameters:
128
- message: Commit message text
129
- args: Additional git commit arguments
130
131
Returns:
132
Command execution result
133
134
Raises:
135
GitCommandError: If commit creation fails
136
CommitMessageLengthError: If message exceeds limits
137
"""
138
```
139
140
### Command Execution
141
142
Execute shell commands with proper error handling and result capture.
143
144
```python { .api }
145
def run(cmd: str, env: dict[str, str] | None = None) -> Command:
146
"""
147
Execute shell command and return result.
148
149
Parameters:
150
- cmd: Shell command to execute
151
- env: Environment variables for command execution
152
153
Returns:
154
Command object with return_code, stdout, and stderr
155
156
Raises:
157
CommandExecutionError: If command execution fails
158
"""
159
```
160
161
#### Tag Operations
162
163
Create and manage git tags with signing support.
164
165
```python { .api }
166
def tag(
167
name: str,
168
message: str = "",
169
signed: bool = False,
170
annotated: bool = True
171
) -> Command:
172
"""
173
Create git tag.
174
175
Parameters:
176
- name: Tag name
177
- message: Tag message (for annotated tags)
178
- signed: Create GPG signed tag
179
- annotated: Create annotated tag (default)
180
181
Returns:
182
Command execution result
183
184
Raises:
185
GitCommandError: If tag creation fails
186
"""
187
188
def tag_exist(tag: str) -> bool:
189
"""
190
Check if git tag exists.
191
192
Parameters:
193
- tag: Tag name to check
194
195
Returns:
196
True if tag exists in repository
197
"""
198
199
def is_signed_tag(tag_name: str) -> bool:
200
"""
201
Check if tag is GPG signed.
202
203
Parameters:
204
- tag_name: Name of tag to check
205
206
Returns:
207
True if tag has valid GPG signature
208
"""
209
210
def get_latest_tag_name(pattern: str = "*") -> str | None:
211
"""
212
Get the most recent tag name.
213
214
Parameters:
215
- pattern: Tag name pattern filter
216
217
Returns:
218
Latest tag name or None if no tags exist
219
"""
220
```
221
222
### Information Retrieval
223
224
#### Commit History
225
226
Retrieve and analyze commit history with filtering options.
227
228
```python { .api }
229
def get_commits(
230
start: str = "",
231
end: str = "",
232
rev: str = "",
233
paths: list[str] = []
234
) -> list[GitCommit]:
235
"""
236
Get git commit history.
237
238
Parameters:
239
- start: Starting revision (exclusive)
240
- end: Ending revision (inclusive)
241
- rev: Specific revision or range
242
- paths: Limit to changes in specific paths
243
244
Returns:
245
List of GitCommit objects in chronological order
246
247
Raises:
248
GitCommandError: If git log command fails
249
"""
250
251
def get_filenames_in_commit(rev: str) -> list[str]:
252
"""
253
Get list of files changed in specific commit.
254
255
Parameters:
256
- rev: Commit revision identifier
257
258
Returns:
259
List of file paths modified in the commit
260
"""
261
```
262
263
#### Tag Information
264
265
Retrieve tag information and metadata.
266
267
```python { .api }
268
def get_tags() -> list[GitTag]:
269
"""
270
Get all git tags in repository.
271
272
Returns:
273
List of GitTag objects sorted by creation date
274
275
Raises:
276
GitCommandError: If git tag command fails
277
"""
278
```
279
280
### File Handling
281
282
#### Smart File Operations
283
284
Intelligent file handling with git configuration awareness.
285
286
```python { .api }
287
def smart_open(
288
filename: str,
289
encoding: str = "utf-8",
290
eol: str = ""
291
) -> TextIOWrapper:
292
"""
293
Open file with git-aware line ending handling.
294
295
Respects git configuration for core.autocrlf and core.eol settings.
296
Handles different line ending conventions appropriately.
297
298
Parameters:
299
- filename: Path to file
300
- encoding: Text encoding (default: utf-8)
301
- eol: End-of-line format override
302
303
Returns:
304
Text file handle with appropriate line ending handling
305
306
Raises:
307
FileNotFoundError: If file doesn't exist
308
UnicodeDecodeError: If encoding is incorrect
309
"""
310
```
311
312
## Usage Examples
313
314
### Basic Git Operations
315
316
```python
317
from commitizen import git
318
319
# Check if in git repository
320
if git.is_git_project():
321
print("In git repository")
322
root = git.find_git_project_root()
323
print(f"Repository root: {root}")
324
325
# Check staging area status
326
if git.is_staging_clean():
327
print("No staged changes")
328
329
# Add files and commit
330
git.add(["src/", "tests/"])
331
result = git.commit("feat: add new feature")
332
print(f"Commit created: {result.return_code == 0}")
333
334
# Create signed tag
335
git.tag("v1.0.0", "Release version 1.0.0", signed=True)
336
```
337
338
### Working with Commit History
339
340
```python
341
from commitizen import git
342
343
# Get all commits since last tag
344
commits = git.get_commits(start="v0.9.0", end="HEAD")
345
346
for commit in commits:
347
print(f"{commit.rev[:8]}: {commit.title}")
348
print(f"Author: {commit.author} <{commit.author_email}>")
349
print(f"Date: {commit.date}")
350
351
# Get files changed in commit
352
files = git.get_filenames_in_commit(commit.rev)
353
print(f"Files: {', '.join(files)}")
354
print()
355
356
# Get commits in specific path
357
src_commits = git.get_commits(
358
start="v0.9.0",
359
end="HEAD",
360
paths=["src/"]
361
)
362
print(f"Commits affecting src/: {len(src_commits)}")
363
```
364
365
### Tag Management
366
367
```python
368
from commitizen import git
369
370
# Get all tags
371
tags = git.get_tags()
372
for tag in tags:
373
print(f"{tag.name} -> {tag.rev[:8]} ({tag.date})")
374
375
# Check if tag exists
376
if git.tag_exist("v1.0.0"):
377
print("Tag v1.0.0 exists")
378
379
# Check if signed
380
if git.is_signed_tag("v1.0.0"):
381
print("Tag is GPG signed")
382
383
# Get latest tag
384
latest = git.get_latest_tag_name()
385
print(f"Latest tag: {latest}")
386
387
# Get latest tag matching pattern
388
latest_stable = git.get_latest_tag_name("v*.*.*")
389
print(f"Latest stable: {latest_stable}")
390
```
391
392
### File Operations with EOL Handling
393
394
```python
395
from commitizen import git
396
397
# Open file with git-aware line ending handling
398
with git.smart_open("CHANGELOG.md", encoding="utf-8") as f:
399
content = f.read()
400
401
# The file is opened with appropriate line endings based on:
402
# - Git configuration (core.autocrlf, core.eol)
403
# - Platform defaults
404
# - Repository .gitattributes settings
405
406
# Write file with consistent line endings
407
with git.smart_open("VERSION", encoding="utf-8", eol="lf") as f:
408
f.write("1.0.0\n")
409
```
410
411
### Error Handling
412
413
```python
414
from commitizen import git
415
from commitizen.exceptions import (
416
NotAGitProjectError,
417
GitCommandError,
418
CommitMessageLengthError
419
)
420
421
try:
422
# Check repository status
423
if not git.is_git_project():
424
raise NotAGitProjectError("Not in git repository")
425
426
# Attempt to commit
427
result = git.commit("Very long commit message that exceeds the recommended length...")
428
429
except NotAGitProjectError as e:
430
print(f"Git error: {e}")
431
432
except CommitMessageLengthError as e:
433
print(f"Commit message too long: {e}")
434
435
except GitCommandError as e:
436
print(f"Git command failed: {e}")
437
print(f"Return code: {e.return_code}")
438
print(f"Error output: {e.stderr}")
439
```
440
441
### Advanced Git Integration
442
443
```python
444
from commitizen import git
445
from commitizen.cmd import run
446
447
# Custom git operations using command runner
448
def get_current_branch() -> str:
449
"""Get current git branch name."""
450
result = run("git branch --show-current")
451
if result.return_code != 0:
452
raise GitCommandError("Failed to get current branch")
453
return result.out.strip()
454
455
def get_remote_url() -> str:
456
"""Get remote origin URL."""
457
result = run("git remote get-url origin")
458
if result.return_code != 0:
459
raise GitCommandError("Failed to get remote URL")
460
return result.out.strip()
461
462
# Repository information
463
try:
464
branch = get_current_branch()
465
remote = get_remote_url()
466
467
print(f"Current branch: {branch}")
468
print(f"Remote URL: {remote}")
469
470
# Check if branch is up to date
471
if git.is_staging_clean():
472
print("Working directory is clean")
473
474
# Get commits on current branch
475
commits = git.get_commits(rev=f"origin/{branch}..HEAD")
476
print(f"Commits ahead: {len(commits)}")
477
478
except GitCommandError as e:
479
print(f"Git operation failed: {e}")
480
```
481
482
### Git Hook Integration
483
484
```python
485
from commitizen import git
486
from pathlib import Path
487
488
def install_commit_msg_hook():
489
"""Install commit message validation hook."""
490
hooks_dir = git.find_git_project_root() / ".git" / "hooks"
491
hook_file = hooks_dir / "commit-msg"
492
493
hook_content = """#!/bin/sh
494
# Commitizen commit message validation
495
exec commitizen check --commit-msg-file $1
496
"""
497
498
hook_file.write_text(hook_content)
499
hook_file.chmod(0o755)
500
501
print("Commit message hook installed")
502
503
def install_pre_push_hook():
504
"""Install pre-push validation hook."""
505
hooks_dir = git.find_git_project_root() / ".git" / "hooks"
506
hook_file = hooks_dir / "pre-push"
507
508
hook_content = """#!/bin/sh
509
# Commitizen branch validation
510
exec commitizen check --rev-range origin/main..HEAD
511
"""
512
513
hook_file.write_text(hook_content)
514
hook_file.chmod(0o755)
515
516
print("Pre-push hook installed")
517
```