or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced.mdauth.mdconfig.mddiff.mdindex.mdobjects.mdreferences.mdremotes.mdrepository.mdstaging.md

advanced.mddocs/

0

# Advanced Operations

1

2

Advanced Git operations including blame, stashing, submodules, filtering, object database access, and specialized repository operations. These features provide fine-grained control over Git functionality for complex workflows.

3

4

## Capabilities

5

6

### Blame Operations

7

8

Track the origin of each line in a file with detailed authorship information.

9

10

```python { .api }

11

class Blame:

12

@property

13

def hunks(self) -> list[BlameHunk]:

14

"""List of blame hunks"""

15

16

def __len__(self) -> int:

17

"""Number of blame hunks"""

18

19

def __getitem__(self, index: int) -> BlameHunk:

20

"""Get blame hunk by index"""

21

22

def __iter__(self):

23

"""Iterate over blame hunks"""

24

25

def for_line(self, line_no: int) -> BlameHunk:

26

"""Get blame hunk for specific line"""

27

28

class BlameHunk:

29

@property

30

def lines_in_hunk(self) -> int:

31

"""Number of lines in hunk"""

32

33

@property

34

def final_commit_id(self) -> Oid:

35

"""Commit that last modified these lines"""

36

37

@property

38

def final_start_line_number(self) -> int:

39

"""Starting line number in final file"""

40

41

@property

42

def final_signature(self) -> Signature:

43

"""Author signature from final commit"""

44

45

@property

46

def orig_commit_id(self) -> Oid:

47

"""Original commit that introduced these lines"""

48

49

@property

50

def orig_path(self) -> str:

51

"""Original file path"""

52

53

@property

54

def orig_start_line_number(self) -> int:

55

"""Starting line number in original file"""

56

57

@property

58

def orig_signature(self) -> Signature:

59

"""Author signature from original commit"""

60

61

@property

62

def boundary(self) -> bool:

63

"""True if hunk is at boundary of blame range"""

64

65

# Repository blame method

66

class Repository:

67

def blame(

68

self,

69

path: str,

70

flags: int = None,

71

min_match_characters: int = None,

72

newest_commit: Oid = None,

73

oldest_commit: Oid = None,

74

min_line: int = None,

75

max_line: int = None

76

) -> Blame:

77

"""

78

Generate blame information for file.

79

80

Parameters:

81

- path: File path to blame

82

- flags: Blame option flags

83

- min_match_characters: Minimum characters for copy detection

84

- newest_commit: Most recent commit to consider

85

- oldest_commit: Oldest commit to consider

86

- min_line: First line to blame (1-based)

87

- max_line: Last line to blame (1-based)

88

89

Returns:

90

Blame object

91

"""

92

93

# Blame Flag Constants

94

GIT_BLAME_NORMAL: int # Normal blame

95

GIT_BLAME_TRACK_COPIES_SAME_FILE: int # Track copies within file

96

GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES: int # Track moves in same commit

97

GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES: int # Track copies in same commit

98

GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES: int # Track copies across commits

99

GIT_BLAME_FIRST_PARENT: int # Follow only first parent

100

GIT_BLAME_USE_MAILMAP: int # Use mailmap for mapping

101

GIT_BLAME_IGNORE_WHITESPACE: int # Ignore whitespace changes

102

```

103

104

### Stash Operations

105

106

Save and restore working directory changes temporarily.

107

108

```python { .api }

109

class Stash:

110

def save(

111

self,

112

stasher: Signature,

113

message: str,

114

flags: int = None,

115

paths: list[str] = None

116

) -> Oid:

117

"""

118

Save working directory to stash.

119

120

Parameters:

121

- stasher: Signature of person creating stash

122

- message: Stash message

123

- flags: Stash creation flags

124

- paths: Specific paths to stash (None = all)

125

126

Returns:

127

Stash commit OID

128

"""

129

130

def apply(

131

self,

132

index: int,

133

callbacks: 'StashApplyCallbacks' = None,

134

reinstate_index: bool = False

135

):

136

"""Apply stash to working directory"""

137

138

def pop(

139

self,

140

index: int,

141

callbacks: 'StashApplyCallbacks' = None,

142

reinstate_index: bool = False

143

):

144

"""Apply and remove stash"""

145

146

def drop(self, index: int):

147

"""Remove stash without applying"""

148

149

def __len__(self) -> int:

150

"""Number of stashes"""

151

152

def __getitem__(self, index: int) -> tuple[str, Oid]:

153

"""Get stash by index (message, oid)"""

154

155

def __iter__(self):

156

"""Iterate over stashes"""

157

158

# Repository stash access

159

class Repository:

160

@property

161

def stash(self) -> Stash:

162

"""Repository stash collection"""

163

164

# Stash Flag Constants

165

GIT_STASH_DEFAULT: int # Default behavior

166

GIT_STASH_KEEP_INDEX: int # Keep staged changes

167

GIT_STASH_INCLUDE_UNTRACKED: int # Include untracked files

168

GIT_STASH_INCLUDE_IGNORED: int # Include ignored files

169

GIT_STASH_KEEP_ALL: int # Keep all changes

170

171

# Stash Apply Flag Constants

172

GIT_STASH_APPLY_DEFAULT: int # Default apply

173

GIT_STASH_APPLY_REINSTATE_INDEX: int # Restore staged changes

174

175

class StashApplyCallbacks:

176

def apply_progress(self, progress: int) -> int:

177

"""Report apply progress"""

178

return 0

179

180

def checkout_notify(

181

self,

182

why: int,

183

path: str,

184

baseline: DiffFile,

185

target: DiffFile,

186

workdir: DiffFile

187

) -> int:

188

"""Checkout notification callback"""

189

return 0

190

```

191

192

### Submodule Operations

193

194

Manage Git submodules for nested repository structures.

195

196

```python { .api }

197

class Submodule:

198

@property

199

def name(self) -> str:

200

"""Submodule name"""

201

202

@property

203

def path(self) -> str:

204

"""Submodule path in repository"""

205

206

@property

207

def url(self) -> str:

208

"""Submodule remote URL"""

209

210

@property

211

def branch(self) -> str:

212

"""Submodule tracking branch"""

213

214

@property

215

def head_id(self) -> Oid:

216

"""OID of submodule HEAD"""

217

218

@property

219

def index_id(self) -> Oid:

220

"""OID of submodule in index"""

221

222

@property

223

def workdir_id(self) -> Oid:

224

"""OID of submodule in working directory"""

225

226

@property

227

def ignore_rule(self) -> int:

228

"""Submodule ignore rule"""

229

230

@property

231

def update_rule(self) -> int:

232

"""Submodule update rule"""

233

234

def init(self, overwrite: bool = False):

235

"""Initialize submodule"""

236

237

def clone(self, callbacks: 'RemoteCallbacks' = None) -> Repository:

238

"""Clone submodule repository"""

239

240

def update(

241

self,

242

init: bool = False,

243

callbacks: 'RemoteCallbacks' = None

244

):

245

"""Update submodule"""

246

247

def sync(self):

248

"""Sync submodule URL"""

249

250

def reload(self, force: bool = False):

251

"""Reload submodule from config"""

252

253

def status(self) -> int:

254

"""Get submodule status flags"""

255

256

def location(self) -> int:

257

"""Get submodule location flags"""

258

259

class SubmoduleCollection:

260

def add(

261

self,

262

url: str,

263

path: str,

264

use_gitlink: bool = True

265

) -> Submodule:

266

"""Add new submodule"""

267

268

def __getitem__(self, name: str) -> Submodule:

269

"""Get submodule by name or path"""

270

271

def __contains__(self, name: str) -> bool:

272

"""Check if submodule exists"""

273

274

def __iter__(self):

275

"""Iterate over submodules"""

276

277

def __len__(self) -> int:

278

"""Number of submodules"""

279

280

# Repository submodule access

281

class Repository:

282

@property

283

def submodules(self) -> SubmoduleCollection:

284

"""Repository submodules"""

285

286

# Submodule Status Constants

287

GIT_SUBMODULE_STATUS_IN_HEAD: int # Submodule in HEAD

288

GIT_SUBMODULE_STATUS_IN_INDEX: int # Submodule in index

289

GIT_SUBMODULE_STATUS_IN_CONFIG: int # Submodule in config

290

GIT_SUBMODULE_STATUS_IN_WD: int # Submodule in working directory

291

GIT_SUBMODULE_STATUS_INDEX_ADDED: int # Submodule added to index

292

GIT_SUBMODULE_STATUS_INDEX_DELETED: int # Submodule deleted from index

293

GIT_SUBMODULE_STATUS_INDEX_MODIFIED: int # Submodule modified in index

294

GIT_SUBMODULE_STATUS_WD_UNINITIALIZED: int # Submodule not initialized

295

GIT_SUBMODULE_STATUS_WD_ADDED: int # Submodule added to workdir

296

GIT_SUBMODULE_STATUS_WD_DELETED: int # Submodule deleted from workdir

297

GIT_SUBMODULE_STATUS_WD_MODIFIED: int # Submodule modified in workdir

298

299

# Submodule Ignore Constants

300

GIT_SUBMODULE_IGNORE_UNSPECIFIED: int # Use config setting

301

GIT_SUBMODULE_IGNORE_NONE: int # Don't ignore changes

302

GIT_SUBMODULE_IGNORE_UNTRACKED: int # Ignore untracked files

303

GIT_SUBMODULE_IGNORE_DIRTY: int # Ignore dirty working directory

304

GIT_SUBMODULE_IGNORE_ALL: int # Ignore all changes

305

```

306

307

### Filter Operations

308

309

Custom content filtering for clean/smudge operations.

310

311

```python { .api }

312

class Filter:

313

def initialize(self):

314

"""Initialize filter"""

315

return 0

316

317

def shutdown(self):

318

"""Shutdown filter"""

319

320

def check(self, source: 'FilterSource', attr_values: list[str]) -> int:

321

"""Check if filter should apply"""

322

return 0

323

324

def apply(

325

self,

326

source: 'FilterSource',

327

to: 'FilterSource',

328

input_data: bytes

329

) -> bytes:

330

"""Apply filter transformation"""

331

return input_data

332

333

def stream(self, source: 'FilterSource', to: 'FilterSource'):

334

"""Stream-based filter application"""

335

pass

336

337

class FilterSource:

338

@property

339

def repository(self) -> Repository:

340

"""Source repository"""

341

342

@property

343

def path(self) -> str:

344

"""File path"""

345

346

@property

347

def file_mode(self) -> int:

348

"""File mode"""

349

350

@property

351

def oid(self) -> Oid:

352

"""File object ID"""

353

354

@property

355

def flags(self) -> int:

356

"""Filter flags"""

357

358

def filter_register(name: str, filter_obj: Filter, priority: int):

359

"""Register custom filter"""

360

361

def filter_unregister(name: str):

362

"""Unregister custom filter"""

363

364

# Filter Mode Constants

365

GIT_FILTER_TO_WORKTREE: int # Smudge (ODB -> workdir)

366

GIT_FILTER_TO_ODB: int # Clean (workdir -> ODB)

367

368

# Filter Flag Constants

369

GIT_FILTER_DEFAULT: int # Default behavior

370

GIT_FILTER_ALLOW_UNSAFE: int # Allow unsafe filters

371

```

372

373

### Object Database Operations

374

375

Low-level access to Git's object database.

376

377

```python { .api }

378

class Odb:

379

def read(self, oid: Oid) -> tuple[int, bytes]:

380

"""

381

Read object from database.

382

383

Returns:

384

Tuple of (object_type, data)

385

"""

386

387

def write(self, data: bytes, object_type: int) -> Oid:

388

"""Write object to database"""

389

390

def exists(self, oid: Oid) -> bool:

391

"""Check if object exists"""

392

393

def refresh(self):

394

"""Refresh object database"""

395

396

def add_backend(self, backend: 'OdbBackend', priority: int):

397

"""Add storage backend"""

398

399

def add_alternate(self, path: str, priority: int):

400

"""Add alternate object database"""

401

402

def __contains__(self, oid: Oid) -> bool:

403

"""Check if object exists"""

404

405

class OdbBackend:

406

"""Base class for ODB backends"""

407

pass

408

409

class OdbBackendLoose(OdbBackend):

410

def __init__(self, objects_dir: str, compression_level: int = -1):

411

"""Loose object backend"""

412

413

class OdbBackendPack(OdbBackend):

414

def __init__(self, objects_dir: str):

415

"""Pack file backend"""

416

417

# Repository ODB access

418

class Repository:

419

@property

420

def odb(self) -> Odb:

421

"""Object database"""

422

423

def init_file_backend(objects_dir: str) -> OdbBackend:

424

"""Initialize file-based ODB backend"""

425

```

426

427

### Pack File Operations

428

429

Build and manage Git pack files for efficient storage.

430

431

```python { .api }

432

class PackBuilder:

433

def __init__(self, repo: Repository):

434

"""Create pack builder"""

435

436

def insert(self, oid: Oid, name: str = None) -> int:

437

"""Insert object into pack"""

438

439

def insert_tree(self, oid: Oid) -> int:

440

"""Insert tree and all referenced objects"""

441

442

def insert_commit(self, oid: Oid) -> int:

443

"""Insert commit and all referenced objects"""

444

445

def insert_walk(self, walk: Walker) -> int:

446

"""Insert objects from walker"""

447

448

def write(self, path: str = None) -> bytes:

449

"""Write pack to file or return pack data"""

450

451

def set_threads(self, threads: int):

452

"""Set number of threads for packing"""

453

454

@property

455

def written_objects_count(self) -> int:

456

"""Number of objects written"""

457

```

458

459

### Advanced Repository Operations

460

461

Specialized repository operations for complex workflows.

462

463

```python { .api }

464

class Repository:

465

# Cherry-pick and Revert

466

def cherry_pick(

467

self,

468

commit: Oid,

469

callbacks: 'CheckoutCallbacks' = None,

470

mainline: int = 0

471

):

472

"""Cherry-pick commit"""

473

474

def revert(

475

self,

476

commit: Oid,

477

callbacks: 'CheckoutCallbacks' = None,

478

mainline: int = 0

479

):

480

"""Revert commit"""

481

482

# Apply Operations

483

def apply(

484

self,

485

diff: Diff,

486

location: int = None,

487

callbacks: 'CheckoutCallbacks' = None

488

):

489

"""Apply diff/patch to repository"""

490

491

# History Operations

492

def descendant_of(self, commit: Oid, ancestor: Oid) -> bool:

493

"""Check if commit is descendant of ancestor"""

494

495

def ahead_behind(self, local: Oid, upstream: Oid) -> tuple[int, int]:

496

"""Count commits ahead/behind between branches"""

497

498

def merge_base(self, one: Oid, two: Oid) -> Oid:

499

"""Find merge base between commits"""

500

501

def merge_base_many(self, oids: list[Oid]) -> Oid:

502

"""Find merge base among multiple commits"""

503

504

# Worktree Operations

505

def create_worktree(

506

self,

507

name: str,

508

path: str,

509

reference: Reference = None

510

) -> Worktree:

511

"""Create new worktree"""

512

513

def worktrees(self) -> list[str]:

514

"""List worktree names"""

515

516

def lookup_worktree(self, name: str) -> Worktree:

517

"""Get worktree by name"""

518

519

class Worktree:

520

@property

521

def name(self) -> str:

522

"""Worktree name"""

523

524

@property

525

def path(self) -> str:

526

"""Worktree path"""

527

528

@property

529

def is_bare(self) -> bool:

530

"""True if worktree is bare"""

531

532

@property

533

def is_detached(self) -> bool:

534

"""True if HEAD is detached"""

535

536

@property

537

def is_locked(self) -> bool:

538

"""True if worktree is locked"""

539

540

def prune(self, flags: int = 0):

541

"""Prune worktree"""

542

543

def lock(self, reason: str = None):

544

"""Lock worktree"""

545

546

def unlock(self):

547

"""Unlock worktree"""

548

```

549

550

### Usage Examples

551

552

#### Blame Analysis

553

554

```python

555

import pygit2

556

557

repo = pygit2.Repository('/path/to/repo')

558

559

# Generate blame for file

560

blame = repo.blame('src/main.py')

561

562

print(f"Blame has {len(blame)} hunks")

563

564

# Analyze each line

565

with open('src/main.py', 'r') as f:

566

lines = f.readlines()

567

568

line_no = 1

569

for hunk in blame:

570

for i in range(hunk.lines_in_hunk):

571

if line_no <= len(lines):

572

line_content = lines[line_no - 1].rstrip()

573

commit = repo[hunk.final_commit_id]

574

author = hunk.final_signature

575

576

print(f"{hunk.final_commit_id.hex[:8]} {author.name:<20} "

577

f"{line_no:4d}: {line_content}")

578

line_no += 1

579

580

# Blame specific line range

581

blame = repo.blame('README.md', min_line=10, max_line=20)

582

583

# Blame with copy detection

584

blame = repo.blame(

585

'renamed_file.py',

586

flags=pygit2.GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES

587

)

588

```

589

590

#### Stash Operations

591

592

```python

593

# Save working directory to stash

594

signature = pygit2.Signature('User', 'user@example.com')

595

stash_id = repo.stash.save(signature, 'Work in progress')

596

print(f"Created stash: {stash_id}")

597

598

# List stashes

599

print("Stashes:")

600

for i, (message, oid) in enumerate(repo.stash):

601

print(f" {i}: {message} ({oid.hex[:8]})")

602

603

# Apply most recent stash

604

if len(repo.stash) > 0:

605

repo.stash.apply(0)

606

607

# Pop stash (apply and remove)

608

if len(repo.stash) > 0:

609

repo.stash.pop(0)

610

611

# Stash with specific options

612

stash_id = repo.stash.save(

613

signature,

614

'Partial stash',

615

flags=pygit2.GIT_STASH_INCLUDE_UNTRACKED | pygit2.GIT_STASH_KEEP_INDEX,

616

paths=['src/*.py']

617

)

618

```

619

620

#### Submodule Management

621

622

```python

623

# Add submodule

624

submodule = repo.submodules.add(

625

'https://github.com/library/repo.git',

626

'lib/external'

627

)

628

629

# Initialize and clone submodule

630

submodule.init()

631

sub_repo = submodule.clone()

632

633

# Update submodule

634

submodule.update()

635

636

# Check submodule status

637

status = submodule.status()

638

if status & pygit2.GIT_SUBMODULE_STATUS_WD_MODIFIED:

639

print("Submodule has uncommitted changes")

640

641

# Iterate over submodules

642

for name, submodule in repo.submodules:

643

print(f"Submodule {name}: {submodule.url} -> {submodule.path}")

644

print(f" Status: {submodule.status()}")

645

```

646

647

#### Custom Filters

648

649

```python

650

class UppercaseFilter(pygit2.Filter):

651

def apply(self, source, to, input_data):

652

# Convert to uppercase when checking out

653

if to == pygit2.GIT_FILTER_TO_WORKTREE:

654

return input_data.upper()

655

# Convert to lowercase when checking in

656

else:

657

return input_data.lower()

658

659

# Register filter

660

pygit2.filter_register('uppercase', UppercaseFilter(), 100)

661

662

# Filter is applied based on .gitattributes:

663

# *.txt filter=uppercase

664

```

665

666

#### Object Database Operations

667

668

```python

669

# Direct object access

670

odb = repo.odb

671

672

# Check if object exists

673

if odb.exists(some_oid):

674

obj_type, data = odb.read(some_oid)

675

print(f"Object type: {obj_type}, size: {len(data)}")

676

677

# Write object directly

678

blob_data = b"Hello, World!"

679

blob_oid = odb.write(blob_data, pygit2.GIT_OBJECT_BLOB)

680

681

# Add alternate object database

682

odb.add_alternate('/path/to/other/repo/.git/objects', 1)

683

```

684

685

#### Pack File Creation

686

687

```python

688

# Create pack file

689

pack_builder = pygit2.PackBuilder(repo)

690

691

# Add objects to pack

692

for commit in repo.walk(repo.head.target):

693

pack_builder.insert(commit.oid)

694

pack_builder.insert_tree(commit.tree_id)

695

696

# Write pack

697

pack_data = pack_builder.write()

698

with open('custom.pack', 'wb') as f:

699

f.write(pack_data)

700

701

print(f"Packed {pack_builder.written_objects_count} objects")

702

```

703

704

#### Advanced Repository Analysis

705

706

```python

707

# Find merge base

708

main_oid = repo.branches['main'].target

709

feature_oid = repo.branches['feature'].target

710

merge_base = repo.merge_base(main_oid, feature_oid)

711

print(f"Merge base: {merge_base}")

712

713

# Count commits ahead/behind

714

ahead, behind = repo.ahead_behind(feature_oid, main_oid)

715

print(f"Feature is {ahead} commits ahead, {behind} commits behind main")

716

717

# Check ancestry

718

if repo.descendant_of(feature_oid, main_oid):

719

print("Feature branch is descendant of main")

720

721

# Cherry-pick commit

722

commit_to_pick = repo.revparse_single('feature~3')

723

repo.cherry_pick(commit_to_pick.oid)

724

725

# Create worktree

726

worktree = repo.create_worktree('feature-test', '/tmp/feature-test')

727

print(f"Created worktree: {worktree.name} at {worktree.path}")

728

```