Reference Transactions
Reference transactions allow you to update multiple references atomically. All reference updates within a transaction either succeed together or fail together, ensuring repository consistency.
Basic Usage
Use the Repository.transaction() method as a context manager. The
transaction commits automatically when the context exits successfully, or
rolls back if an exception is raised:
with repo.transaction() as txn:
txn.lock_ref('refs/heads/master')
txn.set_target('refs/heads/master', new_oid, message='Update master')
Atomic Multi-Reference Updates
Transactions are useful when you need to update multiple references atomically:
# Swap two branches atomically
with repo.transaction() as txn:
txn.lock_ref('refs/heads/branch-a')
txn.lock_ref('refs/heads/branch-b')
# Get current targets
ref_a = repo.lookup_reference('refs/heads/branch-a')
ref_b = repo.lookup_reference('refs/heads/branch-b')
# Swap them
txn.set_target('refs/heads/branch-a', ref_b.target, message='Swap')
txn.set_target('refs/heads/branch-b', ref_a.target, message='Swap')
Automatic Rollback
If an exception occurs during the transaction, changes are automatically rolled back:
try:
with repo.transaction() as txn:
txn.lock_ref('refs/heads/master')
txn.set_target('refs/heads/master', new_oid)
# If this raises an exception, the ref update is rolled back
validate_commit(new_oid)
except ValidationError:
# Master still points to its original target
pass
Manual Commit
While the context manager is recommended, you can manually manage transactions:
from pygit2 import ReferenceTransaction
txn = ReferenceTransaction(repo)
try:
txn.lock_ref('refs/heads/master')
txn.set_target('refs/heads/master', new_oid, message='Update')
txn.commit()
finally:
del txn # Ensure transaction is freed
API Reference
Repository Methods
- Repository.transaction() ReferenceTransaction
Create a new reference transaction.
Returns a context manager that commits all reference updates atomically when the context exits successfully, or performs no updates if an exception is raised.
Example:
with repo.transaction() as txn: txn.lock_ref('refs/heads/master') txn.set_target('refs/heads/master', new_oid, message='Update')
The ReferenceTransaction Type
- class pygit2.ReferenceTransaction(repository: BaseRepository)
Context manager for transactional reference updates.
A transaction allows multiple reference updates to be performed atomically. All updates are applied when the transaction is committed, or none are applied if the transaction is rolled back.
- Example:
- with repo.transaction() as txn:
txn.lock_ref(‘refs/heads/master’) txn.set_target(‘refs/heads/master’, new_oid, message=’Update master’) # Changes committed automatically on context exit
- commit() None
Commit the transaction, applying all queued updates.
- lock_ref(refname: str) None
Lock a reference in preparation for updating it.
- Args:
refname: Name of the reference to lock (e.g., ‘refs/heads/master’)
- remove(refname: str) None
Remove a reference.
The reference must be locked first via lock_ref().
- Args:
refname: Name of the reference to remove
- set_symbolic_target(refname: str, target: str, signature: Signature | None = None, message: str | None = None) None
Set the target of a symbolic reference.
The reference must be locked first via lock_ref().
- Args:
refname: Name of the reference to update target: Target reference name (e.g., ‘refs/heads/master’) signature: Signature for the reflog (None to use repo identity) message: Message for the reflog
- set_target(refname: str, target: Oid | str, signature: Signature | None = None, message: str | None = None) None
Set the target of a direct reference.
The reference must be locked first via lock_ref().
- Args:
refname: Name of the reference to update target: Target OID or hex string signature: Signature for the reflog (None to use repo identity) message: Message for the reflog
Usage Notes
Always lock a reference with
lock_ref()before modifying itTransactions operate on reference names, not Reference objects
Symbolic references can be updated with
set_symbolic_target()References can be deleted with
remove()The signature parameter defaults to the repository’s configured identity
Thread Safety
Transactions are thread-local and must be used from the thread that created
them. Attempting to use a transaction from a different thread raises
RuntimeError:
# This is safe - each thread has its own transaction
def thread1():
with repo.transaction() as txn:
txn.lock_ref('refs/heads/branch1')
txn.set_target('refs/heads/branch1', oid1)
def thread2():
with repo.transaction() as txn:
txn.lock_ref('refs/heads/branch2')
txn.set_target('refs/heads/branch2', oid2)
# Both threads can run concurrently without conflicts
Different threads can hold transactions simultaneously as long as they don’t attempt to lock the same references. If two threads try to acquire locks in different orders, libgit2 will detect potential deadlocks and raise an error.