0
# Remote Operations
1
2
Git remote repository operations including fetching, pushing, and remote management. Handles authentication, transfer progress, and remote reference management for distributed Git workflows.
3
4
## Capabilities
5
6
### Remote Management
7
8
Manage connections to remote repositories with support for multiple remotes and various protocols.
9
10
```python { .api }
11
class Remote:
12
@property
13
def name(self) -> str:
14
"""Remote name"""
15
16
@property
17
def url(self) -> str:
18
"""Remote URL"""
19
20
@property
21
def push_url(self) -> str:
22
"""Push URL (may differ from fetch URL)"""
23
24
@property
25
def refspecs(self) -> list[str]:
26
"""List of refspecs"""
27
28
@property
29
def fetch_refspecs(self) -> list[str]:
30
"""Fetch refspecs"""
31
32
@property
33
def push_refspecs(self) -> list[str]:
34
"""Push refspecs"""
35
36
# Remote Operations
37
def fetch(
38
self,
39
refspecs: list[str] = None,
40
message: str = None,
41
callbacks: 'RemoteCallbacks' = None,
42
prune: int = None,
43
update_fetchhead: bool = True
44
):
45
"""
46
Fetch from remote repository.
47
48
Parameters:
49
- refspecs: Refspecs to fetch (None = use remote's refspecs)
50
- message: Reflog message
51
- callbacks: Remote operation callbacks
52
- prune: Prune tracking branches (GIT_FETCH_PRUNE, GIT_FETCH_NO_PRUNE)
53
- update_fetchhead: Update FETCH_HEAD
54
"""
55
56
def push(
57
self,
58
refspecs: list[str],
59
callbacks: 'RemoteCallbacks' = None
60
):
61
"""
62
Push to remote repository.
63
64
Parameters:
65
- refspecs: Refspecs to push
66
- callbacks: Remote operation callbacks
67
"""
68
69
def upload(
70
self,
71
refspecs: list[str],
72
callbacks: 'RemoteCallbacks' = None
73
):
74
"""Upload pack to remote (push without updating refs)"""
75
76
def ls_remotes(self, callbacks: 'RemoteCallbacks' = None) -> list['RemoteHead']:
77
"""List remote references"""
78
79
def prune(self, callbacks: 'RemoteCallbacks' = None):
80
"""Prune remote tracking branches"""
81
82
def connect(self, direction: int, callbacks: 'RemoteCallbacks' = None):
83
"""Connect to remote"""
84
85
def disconnect(self):
86
"""Disconnect from remote"""
87
88
def download(self, refspecs: list[str] = None, callbacks: 'RemoteCallbacks' = None):
89
"""Download pack from remote"""
90
91
def update_tips(self, callbacks: 'RemoteCallbacks' = None, message: str = None):
92
"""Update remote tracking references"""
93
94
# Configuration
95
def add_fetch_refspec(self, refspec: str):
96
"""Add fetch refspec"""
97
98
def add_push_refspec(self, refspec: str):
99
"""Add push refspec"""
100
101
def set_url(self, url: str):
102
"""Set remote URL"""
103
104
def set_push_url(self, url: str):
105
"""Set push URL"""
106
107
# Remote Direction Constants
108
GIT_DIRECTION_FETCH: int # Fetch direction
109
GIT_DIRECTION_PUSH: int # Push direction
110
111
# Fetch Prune Constants
112
GIT_FETCH_PRUNE_UNSPECIFIED: int # Use config setting
113
GIT_FETCH_PRUNE: int # Prune tracking branches
114
GIT_FETCH_NO_PRUNE: int # Don't prune tracking branches
115
```
116
117
### Remote Collection
118
119
Manage multiple remotes in a repository.
120
121
```python { .api }
122
class RemoteCollection:
123
def create(self, name: str, url: str) -> Remote:
124
"""Create new remote"""
125
126
def delete(self, name: str):
127
"""Delete remote"""
128
129
def rename(self, name: str, new_name: str) -> list[str]:
130
"""
131
Rename remote.
132
133
Returns:
134
List of non-default refspecs that couldn't be renamed
135
"""
136
137
def set_url(self, name: str, url: str):
138
"""Set remote URL"""
139
140
def set_push_url(self, name: str, url: str):
141
"""Set remote push URL"""
142
143
def add_fetch_refspec(self, name: str, refspec: str):
144
"""Add fetch refspec to remote"""
145
146
def add_push_refspec(self, name: str, refspec: str):
147
"""Add push refspec to remote"""
148
149
def get(self, name: str) -> Remote:
150
"""Get remote by name"""
151
152
def __getitem__(self, name: str) -> Remote:
153
"""Get remote by name"""
154
155
def __contains__(self, name: str) -> bool:
156
"""Check if remote exists"""
157
158
def __iter__(self):
159
"""Iterate over remotes"""
160
161
def __len__(self) -> int:
162
"""Number of remotes"""
163
164
# Repository remote access
165
class Repository:
166
@property
167
def remotes(self) -> RemoteCollection:
168
"""Repository remotes collection"""
169
```
170
171
### Remote References
172
173
Information about references on remote repositories.
174
175
```python { .api }
176
class RemoteHead:
177
@property
178
def name(self) -> str:
179
"""Reference name"""
180
181
@property
182
def oid(self) -> Oid:
183
"""Reference target OID"""
184
185
@property
186
def local_oid(self) -> Oid:
187
"""Local OID for this reference"""
188
189
@property
190
def is_local(self) -> bool:
191
"""True if reference exists locally"""
192
193
class PushUpdate:
194
@property
195
def src_refname(self) -> str:
196
"""Source reference name"""
197
198
@property
199
def dst_refname(self) -> str:
200
"""Destination reference name"""
201
202
@property
203
def src(self) -> Oid:
204
"""Source OID"""
205
206
@property
207
def dst(self) -> Oid:
208
"""Destination OID"""
209
```
210
211
### Transfer Progress
212
213
Track progress of remote operations like fetch and push.
214
215
```python { .api }
216
class TransferProgress:
217
@property
218
def total_objects(self) -> int:
219
"""Total objects to transfer"""
220
221
@property
222
def indexed_objects(self) -> int:
223
"""Objects indexed so far"""
224
225
@property
226
def received_objects(self) -> int:
227
"""Objects received so far"""
228
229
@property
230
def local_objects(self) -> int:
231
"""Local objects"""
232
233
@property
234
def total_deltas(self) -> int:
235
"""Total deltas to process"""
236
237
@property
238
def indexed_deltas(self) -> int:
239
"""Deltas indexed so far"""
240
241
@property
242
def received_bytes(self) -> int:
243
"""Bytes received"""
244
```
245
246
### Remote Callbacks
247
248
Callback interface for handling remote operations, authentication, and progress.
249
250
```python { .api }
251
class RemoteCallbacks:
252
def credentials(
253
self,
254
url: str,
255
username_from_url: str,
256
allowed_types: int
257
):
258
"""
259
Provide credentials for authentication.
260
261
Parameters:
262
- url: Remote URL
263
- username_from_url: Username from URL
264
- allowed_types: Credential types allowed
265
266
Returns:
267
Credential object or None
268
"""
269
return None
270
271
def progress(self, stats: TransferProgress) -> bool:
272
"""
273
Report transfer progress.
274
275
Parameters:
276
- stats: Transfer progress information
277
278
Returns:
279
True to continue, False to cancel
280
"""
281
return True
282
283
def update_tips(
284
self,
285
refname: str,
286
old: Oid,
287
new: Oid
288
) -> int:
289
"""
290
Called when updating references.
291
292
Parameters:
293
- refname: Reference name
294
- old: Old OID
295
- new: New OID
296
297
Returns:
298
0 for success, non-zero for error
299
"""
300
return 0
301
302
def completion(self, completion_type: int) -> int:
303
"""
304
Called on operation completion.
305
306
Parameters:
307
- completion_type: Type of completed operation
308
309
Returns:
310
0 for success
311
"""
312
return 0
313
314
def push_update_reference(
315
self,
316
refname: str,
317
message: str
318
) -> int:
319
"""
320
Called for each push update.
321
322
Parameters:
323
- refname: Reference name
324
- message: Error message (if any)
325
326
Returns:
327
0 for success
328
"""
329
return 0
330
331
def pack_progress(
332
self,
333
stage: int,
334
current: int,
335
total: int
336
) -> int:
337
"""Report pack progress"""
338
return 0
339
340
def sideband_progress(self, string: str) -> int:
341
"""Handle sideband progress messages"""
342
return 0
343
344
def certificate_check(
345
self,
346
certificate,
347
valid: bool,
348
host: str
349
) -> bool:
350
"""
351
Check server certificate.
352
353
Returns:
354
True to accept certificate
355
"""
356
return valid
357
```
358
359
### Refspecs
360
361
Refspec objects define how references are mapped between local and remote repositories.
362
363
```python { .api }
364
class Refspec:
365
@property
366
def src(self) -> str:
367
"""Source reference pattern"""
368
369
@property
370
def dst(self) -> str:
371
"""Destination reference pattern"""
372
373
@property
374
def force(self) -> bool:
375
"""True if refspec forces update"""
376
377
@property
378
def string(self) -> str:
379
"""Refspec string representation"""
380
381
@property
382
def direction(self) -> int:
383
"""Refspec direction (fetch or push)"""
384
385
def src_matches(self, refname: str) -> bool:
386
"""Check if refname matches source pattern"""
387
388
def dst_matches(self, refname: str) -> bool:
389
"""Check if refname matches destination pattern"""
390
391
def transform(self, refname: str) -> str:
392
"""Transform refname using refspec"""
393
394
def rtransform(self, refname: str) -> str:
395
"""Reverse transform refname using refspec"""
396
```
397
398
### Usage Examples
399
400
#### Basic Remote Operations
401
402
```python
403
import pygit2
404
405
repo = pygit2.Repository('/path/to/repo')
406
407
# List remotes
408
print("Remotes:")
409
for remote in repo.remotes:
410
print(f" {remote.name}: {remote.url}")
411
412
# Add new remote
413
origin = repo.remotes.create('origin', 'https://github.com/user/repo.git')
414
415
# Get existing remote
416
origin = repo.remotes['origin']
417
print(f"Origin URL: {origin.url}")
418
419
# Set different push URL
420
origin.set_push_url('git@github.com:user/repo.git')
421
```
422
423
#### Fetching from Remote
424
425
```python
426
# Simple fetch
427
origin = repo.remotes['origin']
428
origin.fetch()
429
430
# Fetch with custom refspecs
431
origin.fetch(['refs/heads/*:refs/remotes/origin/*'])
432
433
# Fetch with callbacks
434
class FetchCallbacks(pygit2.RemoteCallbacks):
435
def progress(self, stats):
436
print(f"Received {stats.received_objects}/{stats.total_objects} objects")
437
return True
438
439
def credentials(self, url, username_from_url, allowed_types):
440
if allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY:
441
return pygit2.Keypair('git', '~/.ssh/id_rsa.pub', '~/.ssh/id_rsa', '')
442
return None
443
444
callbacks = FetchCallbacks()
445
origin.fetch(callbacks=callbacks)
446
```
447
448
#### Pushing to Remote
449
450
```python
451
# Push specific branch
452
origin.push(['refs/heads/main:refs/heads/main'])
453
454
# Push all branches
455
origin.push(['refs/heads/*:refs/heads/*'])
456
457
# Push with callbacks
458
class PushCallbacks(pygit2.RemoteCallbacks):
459
def push_update_reference(self, refname, message):
460
if message:
461
print(f"Error pushing {refname}: {message}")
462
return -1
463
else:
464
print(f"Successfully pushed {refname}")
465
return 0
466
467
def credentials(self, url, username_from_url, allowed_types):
468
return pygit2.UserPass('username', 'password')
469
470
callbacks = PushCallbacks()
471
origin.push(['refs/heads/feature:refs/heads/feature'], callbacks=callbacks)
472
```
473
474
#### Listing Remote References
475
476
```python
477
# List remote references
478
remote_refs = origin.ls_remotes()
479
print("Remote references:")
480
for ref in remote_refs:
481
print(f" {ref.name}: {ref.oid}")
482
if ref.is_local:
483
print(f" (local: {ref.local_oid})")
484
```
485
486
#### Advanced Remote Operations
487
488
```python
489
# Clone with custom callbacks
490
class CloneCallbacks(pygit2.RemoteCallbacks):
491
def progress(self, stats):
492
if stats.total_objects > 0:
493
percent = (stats.received_objects * 100) // stats.total_objects
494
print(f"Cloning: {percent}% ({stats.received_objects}/{stats.total_objects})")
495
return True
496
497
def credentials(self, url, username_from_url, allowed_types):
498
if 'github.com' in url:
499
if allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY:
500
return pygit2.Keypair('git', '~/.ssh/id_rsa.pub', '~/.ssh/id_rsa', '')
501
return None
502
503
callbacks = CloneCallbacks()
504
repo = pygit2.clone_repository(
505
'git@github.com:user/repo.git',
506
'/local/path',
507
callbacks=callbacks
508
)
509
510
# Prune remote tracking branches
511
origin.prune()
512
513
# Update remote URL
514
repo.remotes.set_url('origin', 'https://new-url.com/repo.git')
515
516
# Add additional fetch refspec
517
origin.add_fetch_refspec('refs/pull/*/head:refs/remotes/origin/pr/*')
518
```
519
520
#### Working with Refspecs
521
522
```python
523
# Examine refspecs
524
for refspec_str in origin.fetch_refspecs:
525
refspec = pygit2.Refspec(refspec_str)
526
print(f"Refspec: {refspec.string}")
527
print(f" Source: {refspec.src}")
528
print(f" Destination: {refspec.dst}")
529
print(f" Force: {refspec.force}")
530
531
# Test refspec matching
532
if refspec.src_matches('refs/heads/main'):
533
transformed = refspec.transform('refs/heads/main')
534
print(f" 'refs/heads/main' -> '{transformed}'")
535
536
# Add custom refspecs
537
origin.add_fetch_refspec('refs/tags/*:refs/tags/*')
538
origin.add_push_refspec('refs/heads/main:refs/heads/master')
539
```
540
541
#### Authentication Examples
542
543
```python
544
# SSH key authentication
545
class SSHCallbacks(pygit2.RemoteCallbacks):
546
def credentials(self, url, username_from_url, allowed_types):
547
if allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY:
548
return pygit2.Keypair(
549
'git',
550
'/home/user/.ssh/id_rsa.pub',
551
'/home/user/.ssh/id_rsa',
552
'passphrase'
553
)
554
return None
555
556
# Username/password authentication
557
class HTTPSCallbacks(pygit2.RemoteCallbacks):
558
def credentials(self, url, username_from_url, allowed_types):
559
if allowed_types & pygit2.GIT_CREDENTIAL_USERPASS_PLAINTEXT:
560
return pygit2.UserPass('username', 'personal_access_token')
561
return None
562
563
# SSH agent authentication
564
class AgentCallbacks(pygit2.RemoteCallbacks):
565
def credentials(self, url, username_from_url, allowed_types):
566
if allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY:
567
return pygit2.KeypairFromAgent('git')
568
return None
569
```
570
571
#### Progress Monitoring
572
573
```python
574
class DetailedCallbacks(pygit2.RemoteCallbacks):
575
def __init__(self):
576
self.total_bytes = 0
577
self.start_time = time.time()
578
579
def progress(self, stats):
580
# Calculate transfer rate
581
elapsed = time.time() - self.start_time
582
if elapsed > 0:
583
rate = stats.received_bytes / elapsed / 1024 # KB/s
584
else:
585
rate = 0
586
587
print(f"\rObjects: {stats.received_objects}/{stats.total_objects} "
588
f"Deltas: {stats.indexed_deltas}/{stats.total_deltas} "
589
f"Rate: {rate:.1f} KB/s", end='')
590
591
return True
592
593
def sideband_progress(self, string):
594
print(f"Remote: {string.strip()}")
595
return 0
596
597
callbacks = DetailedCallbacks()
598
origin.fetch(callbacks=callbacks)
599
print() # New line after progress
600
```