or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

chooser.mdcommand-line.mdconfiguration.mddiff-utilities.mdfile-utilities.mdformatters.mdgit-integration.mdindex.mdmain-functions.mdpreprocessors.mdverification.md

git-integration.mddocs/

0

# Git Integration

1

2

Git repository interaction, revision comparison, and modified file discovery for determining which files and regions need formatting.

3

4

## Capabilities

5

6

### Repository Detection

7

8

Functions for detecting and validating Git repositories.

9

10

```python { .api }

11

def git_is_repository(path: Path) -> bool:

12

"""

13

Check if the given path is inside a Git working tree.

14

15

Parameters:

16

- path: Path to check for Git repository

17

18

Returns:

19

True if path is within a Git repository, False otherwise

20

"""

21

22

def get_path_in_repo(path: Path) -> str:

23

"""

24

Get relative path within repository, handling VSCode temp files.

25

26

Converts absolute paths to repository-relative paths and handles

27

special cases like VSCode temporary files.

28

29

Parameters:

30

- path: Absolute path to convert

31

32

Returns:

33

Repository-relative path as string

34

"""

35

```

36

37

### Modified File Discovery

38

39

Functions for finding modified Python files using Git.

40

41

```python { .api }

42

def git_get_modified_python_files(

43

paths: Collection[str],

44

revrange: RevisionRange,

45

cwd: Path,

46

) -> Set[str]:

47

"""

48

Ask Git for modified *.py files in the given revision range.

49

50

Parameters:

51

- paths: Collection of file/directory paths to check

52

- revrange: Git revision range for comparison

53

- cwd: Current working directory for Git operations

54

55

Returns:

56

Set of repository-relative paths to modified Python files

57

"""

58

59

def should_reformat_file(path: Path) -> bool:

60

"""

61

Check if the given path is an existing *.py file that should be reformatted.

62

63

Parameters:

64

- path: File path to check

65

66

Returns:

67

True if file exists and is a Python file, False otherwise

68

"""

69

70

def get_missing_at_revision(paths: Set[str], revrange: RevisionRange, cwd: Path) -> Set[str]:

71

"""

72

Return paths that are missing (don't exist) in the given revision.

73

74

Parameters:

75

- paths: Set of paths to check

76

- revrange: Git revision range

77

- cwd: Current working directory

78

79

Returns:

80

Set of paths that don't exist in the specified revision

81

"""

82

```

83

84

### Line-Level Change Detection

85

86

Class for detecting changed lines between Git revisions.

87

88

```python { .api }

89

class EditedLinenumsDiffer:

90

"""

91

Find changed lines between Git revisions for selective formatting.

92

93

This class compares file content between revisions to identify

94

exactly which lines have been modified, enabling precise formatting

95

of only the changed regions.

96

"""

97

98

def __init__(self, root: Path, revrange: RevisionRange):

99

"""

100

Initialize the differ with root path and revision range.

101

102

Parameters:

103

- root: Common root directory for relative paths

104

- revrange: Git revision range for comparison

105

"""

106

107

def revision_vs_lines(

108

self,

109

path_in_repo: Path,

110

content: TextDocument,

111

context_lines: int

112

) -> List[int]:

113

"""

114

Return changed line numbers between revision and current content.

115

116

Parameters:

117

- path_in_repo: Repository-relative path to the file

118

- content: Current content of the file

119

- context_lines: Number of context lines to include around changes

120

121

Returns:

122

List of line numbers that have changed in the current content

123

"""

124

```

125

126

## Usage Examples

127

128

### Basic Repository Operations

129

130

```python

131

from darker.git import git_is_repository, get_path_in_repo

132

from pathlib import Path

133

134

# Check if current directory is a Git repository

135

if git_is_repository(Path.cwd()):

136

print("Current directory is in a Git repository")

137

138

# Get repository-relative path

139

abs_path = Path.cwd() / "src" / "module.py"

140

repo_path = get_path_in_repo(abs_path)

141

print(f"Repository path: {repo_path}")

142

else:

143

print("Not in a Git repository")

144

```

145

146

### Finding Modified Files

147

148

```python

149

from darker.git import git_get_modified_python_files, should_reformat_file

150

from darkgraylib.git import RevisionRange

151

from pathlib import Path

152

153

# Set up revision range

154

revrange = RevisionRange.parse_with_common_ancestor("HEAD~1", ":WORKTREE:")

155

cwd = Path.cwd()

156

157

# Find modified Python files

158

modified_files = git_get_modified_python_files(

159

paths=["src/", "tests/"],

160

revrange=revrange,

161

cwd=cwd

162

)

163

164

print("Modified Python files:")

165

for file_path in modified_files:

166

full_path = cwd / file_path

167

if should_reformat_file(full_path):

168

print(f" {file_path}")

169

```

170

171

### Line-Level Change Detection

172

173

```python

174

from darker.git import EditedLinenumsDiffer

175

from darkgraylib.git import RevisionRange

176

from darkgraylib.utils import TextDocument

177

from pathlib import Path

178

179

# Set up differ

180

revrange = RevisionRange.parse_with_common_ancestor("HEAD", ":WORKTREE:")

181

differ = EditedLinenumsDiffer(Path.cwd(), revrange)

182

183

# Read current file content

184

file_path = "src/module.py"

185

with open(file_path) as f:

186

current_content = TextDocument.from_str(f.read())

187

188

# Find changed lines

189

changed_lines = differ.revision_vs_lines(

190

Path(file_path),

191

current_content,

192

context_lines=3

193

)

194

195

print(f"Changed lines in {file_path}: {changed_lines}")

196

197

# Use line information for selective formatting

198

if changed_lines:

199

print("File has changes and needs formatting")

200

else:

201

print("No changes detected, skipping formatting")

202

```

203

204

### Integration with Main Workflow

205

206

```python

207

from darker.git import EditedLinenumsDiffer, git_get_modified_python_files

208

from darker.chooser import choose_lines

209

from darkgraylib.git import RevisionRange

210

from darkgraylib.utils import TextDocument

211

from pathlib import Path

212

213

def format_only_changed_lines(file_path: str, formatter_func):

214

"""Example of using Git integration for selective formatting."""

215

216

# Set up Git integration

217

revrange = RevisionRange.parse_with_common_ancestor("HEAD", ":WORKTREE:")

218

differ = EditedLinenumsDiffer(revrange, Path.cwd())

219

220

# Read current content

221

with open(file_path) as f:

222

current_content = TextDocument.from_str(f.read())

223

224

# Find changed lines

225

baseline_lines, edited_lines = differ.revision_vs_lines(

226

file_path,

227

current_content

228

)

229

230

if not edited_lines:

231

print(f"No changes in {file_path}, skipping")

232

return current_content

233

234

# Apply formatter to entire content

235

formatted_content = formatter_func(current_content)

236

237

# Select only changed regions from formatted version

238

result = choose_lines(

239

edited_lines,

240

current_content,

241

formatted_content

242

)

243

244

return result

245

246

# Usage

247

formatted = format_only_changed_lines(

248

"src/module.py",

249

lambda content: run_black_formatter(content)

250

)

251

```

252

253

### Working with Missing Files

254

255

```python

256

from darker.git import get_missing_at_revision

257

from darkgraylib.git import RevisionRange

258

from pathlib import Path

259

260

# Check for files that exist now but not in the baseline

261

revrange = RevisionRange.parse_with_common_ancestor("HEAD~5", ":WORKTREE:")

262

current_files = {"src/new_module.py", "src/existing.py", "tests/test_new.py"}

263

264

missing_in_baseline = get_missing_at_revision(

265

current_files,

266

revrange,

267

Path.cwd()

268

)

269

270

print("New files (missing in baseline):")

271

for new_file in missing_in_baseline:

272

print(f" {new_file}")

273

274

# These files should be fully formatted since they're entirely new

275

existing_files = current_files - missing_in_baseline

276

print("Existing files (partial formatting):")

277

for existing_file in existing_files:

278

print(f" {existing_file}")

279

```

280

281

### Custom Revision Ranges

282

283

```python

284

from darker.git import git_get_modified_python_files

285

from darkgraylib.git import RevisionRange

286

from pathlib import Path

287

288

# Different revision range patterns

289

examples = [

290

"HEAD~1", # Compare with previous commit

291

"main", # Compare with main branch

292

"origin/main...", # Compare with remote main branch

293

":PRE-COMMIT:", # Pre-commit hook mode

294

":STDIN:", # Standard input mode

295

]

296

297

for rev_str in examples:

298

try:

299

revrange = RevisionRange.parse_with_common_ancestor(rev_str, ":WORKTREE:")

300

modified = git_get_modified_python_files(

301

paths=["src/"],

302

revrange=revrange,

303

cwd=Path.cwd()

304

)

305

print(f"{rev_str}: {len(modified)} modified files")

306

except Exception as e:

307

print(f"{rev_str}: Error - {e}")

308

```