or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli.mdclients.mdconfiguration.mddiff-merge.mdindex-management.mdindex.mdobject-storage.mdobjects.mdpack-files.mdporcelain.mdreferences.mdrepository.md

clients.mddocs/

0

# Git Protocol Clients

1

2

Network protocol implementations for communicating with Git servers over HTTP, SSH, and Git protocols with authentication and progress tracking.

3

4

## Capabilities

5

6

### Base Client Classes

7

8

Abstract base classes and common functionality for Git protocol clients.

9

10

```python { .api }

11

class GitClient:

12

"""Abstract base class for Git protocol clients."""

13

14

def fetch_pack(

15

self,

16

path: str,

17

determine_wants: Callable,

18

graph_walker: object,

19

pack_data: Callable,

20

progress: Optional[Callable] = None,

21

depth: Optional[int] = None

22

) -> FetchPackResult:

23

"""

24

Fetch a pack from the remote repository.

25

26

Args:

27

path: Repository path on remote

28

determine_wants: Function to determine wanted objects

29

graph_walker: Object graph walker

30

pack_data: Callback for pack data

31

progress: Optional progress callback

32

depth: Optional shallow clone depth

33

34

Returns:

35

FetchPackResult with refs and symrefs

36

"""

37

38

def send_pack(

39

self,

40

path: str,

41

determine_wants: Callable,

42

generate_pack_data: Callable,

43

progress: Optional[Callable] = None

44

) -> SendPackResult:

45

"""

46

Send a pack to the remote repository.

47

48

Args:

49

path: Repository path on remote

50

determine_wants: Function to determine wanted objects

51

generate_pack_data: Function to generate pack data

52

progress: Optional progress callback

53

54

Returns:

55

SendPackResult with reference update status

56

"""

57

58

def get_refs(self, path: str) -> Dict[bytes, bytes]:

59

"""

60

Get references from remote repository.

61

62

Args:

63

path: Repository path on remote

64

65

Returns:

66

Dictionary mapping ref names to SHAs

67

"""

68

69

def archive(

70

self,

71

path: str,

72

committish: Optional[str] = None,

73

write_data: Optional[Callable] = None,

74

progress: Optional[Callable] = None,

75

write_error: Optional[Callable] = None,

76

) -> None:

77

"""

78

Retrieve an archive from remote repository.

79

80

Args:

81

path: Repository path on remote

82

committish: Commit-ish to archive

83

write_data: Function to write archive data

84

progress: Optional progress callback

85

write_error: Function to write error messages

86

"""

87

88

def close(self) -> None:

89

"""

90

Close the client and clean up resources.

91

"""

92

93

class TraditionalGitClient(GitClient):

94

"""Traditional Git protocol client implementation.

95

96

Provides common functionality for Git protocol clients

97

that use traditional Git wire protocol.

98

"""

99

100

def __init__(

101

self,

102

can_read: Optional[Callable] = None,

103

read: Optional[Callable] = None,

104

write: Optional[Callable] = None,

105

**kwargs

106

) -> None:

107

"""

108

Initialize traditional Git client.

109

110

Args:

111

can_read: Function to check if data can be read

112

read: Function to read data

113

write: Function to write data

114

"""

115

```

116

117

### Protocol-Specific Clients

118

119

Client implementations for different network protocols.

120

121

```python { .api }

122

class TCPGitClient(TraditionalGitClient):

123

"""TCP-based Git protocol client.

124

125

Connects to Git daemon over TCP socket on specified

126

host and port.

127

"""

128

129

def __init__(

130

self,

131

host: str,

132

port: Optional[int] = None,

133

**kwargs

134

) -> None:

135

"""

136

Initialize TCP Git client.

137

138

Args:

139

host: Git server hostname

140

port: Git server port (default: 9418)

141

"""

142

143

class SubprocessGitClient(TraditionalGitClient):

144

"""Subprocess-based Git client using local git command.

145

146

Executes local git commands in subprocess to communicate

147

with remote repositories.

148

"""

149

150

def __init__(self, **kwargs) -> None:

151

"""

152

Initialize subprocess Git client.

153

"""

154

155

class LocalGitClient(GitClient):

156

"""Local filesystem Git client.

157

158

Accesses Git repositories directly on the local filesystem

159

without network communication.

160

"""

161

162

def __init__(self, **kwargs) -> None:

163

"""

164

Initialize local Git client.

165

"""

166

167

class SSHGitClient(TraditionalGitClient):

168

"""SSH-based Git protocol client.

169

170

Connects to Git repositories over SSH using configurable

171

SSH vendor implementations.

172

"""

173

174

def __init__(

175

self,

176

host: str,

177

port: Optional[int] = None,

178

username: Optional[str] = None,

179

vendor: Optional["SSHVendor"] = None,

180

**kwargs

181

) -> None:

182

"""

183

Initialize SSH Git client.

184

185

Args:

186

host: SSH server hostname

187

port: SSH server port (default: 22)

188

username: SSH username

189

vendor: SSH vendor implementation

190

"""

191

192

class BundleClient(GitClient):

193

"""Git bundle client for reading/writing Git bundles.

194

195

Handles Git bundle format files that contain packaged

196

Git objects and references.

197

"""

198

199

def __init__(self, bundle_file: Union[str, IO]) -> None:

200

"""

201

Initialize bundle client.

202

203

Args:

204

bundle_file: Path to bundle file or file-like object

205

"""

206

```

207

208

### HTTP Clients

209

210

HTTP-based Git protocol clients with authentication support.

211

212

```python { .api }

213

class AbstractHttpGitClient(GitClient):

214

"""Abstract HTTP Git protocol client.

215

216

Base class for HTTP-based Git protocol implementations

217

supporting smart HTTP protocol.

218

"""

219

220

def __init__(

221

self,

222

base_url: str,

223

dumb: Optional[bool] = None,

224

**kwargs

225

) -> None:

226

"""

227

Initialize HTTP Git client.

228

229

Args:

230

base_url: Base URL for Git repository

231

dumb: Force dumb HTTP protocol

232

"""

233

234

def _http_request(

235

self,

236

url: str,

237

headers: Optional[Dict[str, str]] = None,

238

data: Optional[bytes] = None

239

) -> object:

240

"""

241

Make HTTP request.

242

243

Args:

244

url: Request URL

245

headers: Optional HTTP headers

246

data: Optional request body data

247

248

Returns:

249

HTTP response object

250

"""

251

252

def _discover_references(

253

self,

254

service: str,

255

base_url: str

256

) -> tuple[Dict[bytes, bytes], Dict[bytes, bytes]]:

257

"""

258

Discover references from HTTP endpoint.

259

260

Args:

261

service: Git service name

262

base_url: Repository base URL

263

264

Returns:

265

Tuple of (refs, symrefs)

266

"""

267

268

class Urllib3HttpGitClient(AbstractHttpGitClient):

269

"""HTTP Git client using urllib3 library.

270

271

Provides HTTP transport using the urllib3 library

272

with connection pooling and retry logic.

273

"""

274

275

def __init__(

276

self,

277

base_url: str,

278

config: Optional[object] = None,

279

pool_manager: Optional["urllib3.PoolManager"] = None,

280

**kwargs

281

) -> None:

282

"""

283

Initialize urllib3 HTTP client.

284

285

Args:

286

base_url: Base URL for Git repository

287

config: Git configuration object

288

pool_manager: Optional urllib3 PoolManager

289

"""

290

```

291

292

### Transport Functions

293

294

Utility functions for determining appropriate transport clients.

295

296

```python { .api }

297

def get_transport_and_path(uri: str) -> Tuple[GitClient, str]:

298

"""

299

Get appropriate transport client and path for URI.

300

301

Args:

302

uri: Git repository URI

303

304

Returns:

305

Tuple of (client, path)

306

"""

307

308

def get_transport_and_path_from_url(

309

url: str,

310

config: Optional[object] = None,

311

**kwargs

312

) -> Tuple[GitClient, str]:

313

"""

314

Get transport client and path from URL with options.

315

316

Args:

317

url: Repository URL

318

config: Optional Git configuration

319

**kwargs: Additional client options

320

321

Returns:

322

Tuple of (client, path)

323

"""

324

325

def parse_rsync_url(url: str) -> Tuple[Optional[str], str, Optional[str]]:

326

"""

327

Parse rsync-style URL into components.

328

329

Args:

330

url: rsync-style URL (user@host:path)

331

332

Returns:

333

Tuple of (username, host, path)

334

"""

335

336

def default_local_git_client_cls() -> type[GitClient]:

337

"""

338

Get default local Git client class.

339

340

Returns:

341

Git client class for local operations

342

"""

343

344

def default_user_agent_string() -> str:

345

"""

346

Get default user agent string for HTTP clients.

347

348

Returns:

349

User agent string identifying Dulwich

350

"""

351

```

352

353

### SSH Vendor Classes

354

355

SSH implementation backends for SSH-based Git clients.

356

357

```python { .api }

358

class SSHVendor:

359

"""Abstract SSH vendor interface.

360

361

Defines interface for different SSH implementations

362

used by SSH Git clients.

363

"""

364

365

def run_command(

366

self,

367

host: str,

368

command: str,

369

username: Optional[str] = None,

370

port: Optional[int] = None,

371

ssh_key: Optional[str] = None,

372

**kwargs

373

) -> "SubprocessWrapper":

374

"""

375

Run command on remote host via SSH.

376

377

Args:

378

host: Remote hostname

379

command: Command to execute

380

username: SSH username

381

port: SSH port number

382

ssh_key: Path to SSH private key

383

384

Returns:

385

SubprocessWrapper for the SSH connection

386

"""

387

388

class SubprocessSSHVendor(SSHVendor):

389

"""SSH vendor using subprocess to call ssh command.

390

391

Uses the system 'ssh' command to create SSH connections.

392

"""

393

394

def __init__(self, ssh_command: Optional[str] = None) -> None:

395

"""

396

Initialize subprocess SSH vendor.

397

398

Args:

399

ssh_command: Custom SSH command (default: 'ssh')

400

"""

401

402

class PLinkSSHVendor(SSHVendor):

403

"""SSH vendor using PuTTY's plink command.

404

405

Windows-specific SSH vendor that uses PuTTY's plink

406

command for SSH connections.

407

"""

408

409

def __init__(self, plink_command: Optional[str] = None) -> None:

410

"""

411

Initialize PLink SSH vendor.

412

413

Args:

414

plink_command: Custom plink command (default: 'plink')

415

"""

416

417

class SubprocessWrapper:

418

"""

419

Wrapper for subprocess providing Git protocol interface.

420

421

Manages stdin, stdout, stderr streams for Git protocol

422

communication over SSH or local subprocesses.

423

"""

424

425

def __init__(self, proc: subprocess.Popen) -> None:

426

"""

427

Initialize subprocess wrapper.

428

429

Args:

430

proc: Popen subprocess instance

431

"""

432

433

def can_read(self) -> bool:

434

"""

435

Check if data can be read from subprocess.

436

437

Returns:

438

True if data is available to read

439

"""

440

441

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

442

"""

443

Write data to subprocess stdin.

444

445

Args:

446

data: Data to write

447

448

Returns:

449

Number of bytes written

450

"""

451

452

def read(self, size: int = -1) -> bytes:

453

"""

454

Read data from subprocess stdout.

455

456

Args:

457

size: Number of bytes to read

458

459

Returns:

460

Data read from subprocess

461

"""

462

463

def close(self) -> None:

464

"""

465

Close subprocess and wait for termination.

466

"""

467

```

468

469

### Result Classes

470

471

Data classes containing results from Git protocol operations.

472

473

```python { .api }

474

class FetchPackResult:

475

"""Result from fetch_pack operation.

476

477

Contains information about refs and capabilities

478

returned by the remote during fetch.

479

"""

480

481

refs: Dict[bytes, bytes]

482

symrefs: Dict[bytes, bytes]

483

agent: Optional[bytes]

484

shallow: Optional[set[bytes]]

485

new_shallow: Optional[set[bytes]]

486

new_unshallow: Optional[set[bytes]]

487

488

def __init__(

489

self,

490

refs: Dict[bytes, bytes],

491

symrefs: Optional[Dict[bytes, bytes]] = None,

492

agent: Optional[bytes] = None,

493

shallow: Optional[set[bytes]] = None,

494

new_shallow: Optional[set[bytes]] = None,

495

new_unshallow: Optional[set[bytes]] = None,

496

) -> None:

497

"""

498

Initialize fetch pack result.

499

500

Args:

501

refs: Dictionary of references

502

symrefs: Dictionary of symbolic references

503

agent: Remote agent string

504

shallow: Set of shallow commit SHAs

505

new_shallow: Set of newly shallow commits

506

new_unshallow: Set of newly unshallow commits

507

"""

508

509

class SendPackResult:

510

"""Result from send_pack operation.

511

512

Contains status of reference updates sent to remote.

513

"""

514

515

ref_status: Dict[bytes, str]

516

agent: Optional[bytes]

517

pack_sent: bool

518

519

def __init__(

520

self,

521

ref_status: Dict[bytes, str],

522

agent: Optional[bytes] = None,

523

pack_sent: bool = False,

524

) -> None:

525

"""

526

Initialize send pack result.

527

528

Args:

529

ref_status: Status of each reference update

530

agent: Remote agent string

531

pack_sent: Whether pack data was sent

532

"""

533

534

class LsRemoteResult:

535

"""Result from ls_remote operation.

536

537

Contains references and symbolic references from remote.

538

"""

539

540

refs: Dict[bytes, bytes]

541

symrefs: Dict[bytes, bytes]

542

agent: Optional[bytes]

543

544

def __init__(

545

self,

546

refs: Dict[bytes, bytes],

547

symrefs: Optional[Dict[bytes, bytes]] = None,

548

agent: Optional[bytes] = None,

549

) -> None:

550

"""

551

Initialize ls-remote result.

552

553

Args:

554

refs: Dictionary of references

555

symrefs: Dictionary of symbolic references

556

agent: Remote agent string

557

"""

558

559

class ReportStatusParser:

560

"""Parser for Git report-status capability responses.

561

562

Parses status reports from remote during push operations

563

to determine success/failure of reference updates.

564

"""

565

566

def __init__(self) -> None:

567

"""

568

Initialize report status parser.

569

"""

570

571

def check(self) -> Dict[bytes, str]:

572

"""

573

Check status reports and return results.

574

575

Returns:

576

Dictionary mapping refs to status messages

577

"""

578

```

579

580

### Validation Functions

581

582

Functions for validating protocol requests and responses.

583

584

```python { .api }

585

def check_wants(wants: List[bytes], refs: Dict[bytes, bytes]) -> None:

586

"""

587

Validate that wanted objects exist in refs.

588

589

Args:

590

wants: List of wanted object SHAs

591

refs: Available references

592

593

Raises:

594

InvalidWants: If wanted objects are not available

595

"""

596

597

def _fileno_can_read(fileno: int) -> bool:

598

"""

599

Check if file descriptor is ready for reading.

600

601

Args:

602

fileno: File descriptor number

603

604

Returns:

605

True if data can be read without blocking

606

"""

607

608

def _win32_peek_avail(handle: int) -> int:

609

"""

610

Check available bytes in Windows named pipe.

611

612

Args:

613

handle: Windows pipe handle

614

615

Returns:

616

Number of bytes available to read

617

"""

618

```

619

620

## Exception Classes

621

622

```python { .api }

623

class InvalidWants(Exception):

624

"""Exception raised when client wants unavailable objects.

625

626

Occurs when fetch operation requests objects that don't

627

exist in the remote repository.

628

"""

629

630

def __init__(self, wants: List[bytes]) -> None:

631

"""

632

Initialize exception.

633

634

Args:

635

wants: List of unavailable object SHAs

636

"""

637

638

class HTTPUnauthorized(Exception):

639

"""Exception raised for HTTP authentication failures.

640

641

Occurs when HTTP Git operations fail due to insufficient

642

authentication credentials.

643

"""

644

645

def __init__(self, www_authenticate: str, url: str) -> None:

646

"""

647

Initialize exception.

648

649

Args:

650

www_authenticate: WWW-Authenticate header value

651

url: URL that failed authentication

652

"""

653

654

class HTTPProxyUnauthorized(Exception):

655

"""Exception raised for HTTP proxy authentication failures.

656

657

Occurs when HTTP proxy requires authentication that

658

was not provided or was invalid.

659

"""

660

661

def __init__(self, proxy_authenticate: str, url: str) -> None:

662

"""

663

Initialize exception.

664

665

Args:

666

proxy_authenticate: Proxy-Authenticate header value

667

url: URL that failed proxy authentication

668

"""

669

670

class StrangeHostname(Exception):

671

"""Exception raised for malformed hostnames.

672

673

Occurs when hostname in Git URL cannot be parsed

674

or contains invalid characters.

675

"""

676

```

677

678

## Usage Examples

679

680

### Basic Client Usage

681

682

```python

683

from dulwich.client import (

684

get_transport_and_path,

685

get_transport_and_path_from_url,

686

InvalidWants,

687

HTTPUnauthorized

688

)

689

from dulwich.repo import Repo

690

691

# Get appropriate client for URL

692

client, path = get_transport_and_path('https://github.com/user/repo.git')

693

694

# Fetch references

695

try:

696

refs = client.get_refs(path)

697

for ref_name, sha in refs.items():

698

print(f"{ref_name.decode()}: {sha.hex()}")

699

except HTTPUnauthorized as e:

700

print(f"Authentication failed for {e.url}")

701

except InvalidWants as e:

702

print(f"Invalid objects requested: {e}")

703

finally:

704

client.close()

705

706

# Advanced client usage with configuration

707

repo = Repo('/path/to/repo')

708

config = repo.get_config()

709

client, path = get_transport_and_path_from_url(

710

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

711

config=config

712

)

713

714

# Fetch pack with progress

715

def progress_callback(message):

716

print(f"Progress: {message.decode()}")

717

718

def determine_wants(refs, depth=None):

719

# Return list of SHAs to fetch

720

return [refs[b'refs/heads/main']]

721

722

def graph_walker_func():

723

# Return graph walker for the repository

724

return repo.get_graph_walker()

725

726

def pack_data_callback(data):

727

# Process incoming pack data

728

pass

729

730

result = client.fetch_pack(

731

path,

732

determine_wants,

733

graph_walker_func(),

734

pack_data_callback,

735

progress=progress_callback

736

)

737

738

print(f"Fetched {len(result.refs)} references")

739

print(f"Remote agent: {result.agent}")

740

client.close()

741

```

742

743

### Custom SSH Configuration

744

745

```python

746

from dulwich.client import (

747

SSHGitClient,

748

SubprocessSSHVendor,

749

PLinkSSHVendor

750

)

751

import sys

752

753

# Create SSH client with custom vendor

754

if sys.platform == 'win32':

755

ssh_vendor = PLinkSSHVendor()

756

else:

757

ssh_vendor = SubprocessSSHVendor()

758

759

client = SSHGitClient(

760

'git.example.com',

761

username='git',

762

port=2222,

763

vendor=ssh_vendor

764

)

765

766

# Use client for operations

767

try:

768

refs = client.get_refs('/path/to/repo.git')

769

for ref_name, sha in refs.items():

770

print(f"{ref_name.decode()}: {sha.hex()}")

771

772

# Clone repository using SSH

773

def determine_wants(refs, depth=None):

774

return list(refs.values())

775

776

# Fetch all references

777

result = client.fetch_pack(

778

'/path/to/repo.git',

779

determine_wants,

780

None, # graph_walker

781

lambda data: None, # pack_data

782

)

783

print(f"SSH fetch completed: {len(result.refs)} refs")

784

785

finally:

786

client.close()

787

```

788

789

### HTTP Client with Authentication

790

791

```python

792

from dulwich.client import Urllib3HttpGitClient, HTTPUnauthorized

793

import urllib3

794

795

# Create HTTP client with custom pool manager

796

pool_manager = urllib3.PoolManager(

797

cert_reqs='CERT_REQUIRED',

798

ca_certs='/path/to/ca-certificates.crt'

799

)

800

801

client = Urllib3HttpGitClient(

802

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

803

pool_manager=pool_manager

804

)

805

806

# Configure authentication if needed

807

# HTTP clients support various authentication methods:

808

# - Basic auth via URL: https://user:pass@github.com/user/repo.git

809

# - Token auth via environment or config

810

811

try:

812

# Fetch references

813

refs = client.get_refs('')

814

for ref_name, sha in refs.items():

815

print(f"{ref_name.decode()}: {sha.hex()}")

816

817

# Smart HTTP operations

818

def determine_wants(refs, depth=None):

819

# Only fetch main branch

820

if b'refs/heads/main' in refs:

821

return [refs[b'refs/heads/main']]

822

return []

823

824

result = client.fetch_pack(

825

'', # Empty path for base URL

826

determine_wants,

827

None, # graph_walker

828

lambda data: print(f"Received {len(data)} bytes"),

829

)

830

831

print(f"HTTP fetch completed")

832

print(f"Agent: {result.agent}")

833

834

except HTTPUnauthorized as e:

835

print(f"Authentication required: {e.www_authenticate}")

836

print(f"Failed URL: {e.url}")

837

finally:

838

client.close()

839

840

# Bundle client example

841

from dulwich.client import BundleClient

842

843

# Read from bundle file

844

with open('backup.bundle', 'rb') as bundle_file:

845

bundle_client = BundleClient(bundle_file)

846

refs = bundle_client.get_refs('')

847

print(f"Bundle contains {len(refs)} references")

848

```